diff --git a/src/tool/pps/meson.build b/src/tool/pps/meson.build index c379a74c847..9ca5e57e003 100644 --- a/src/tool/pps/meson.build +++ b/src/tool/pps/meson.build @@ -42,3 +42,20 @@ executable( cpp_args: '-std=c++17', install: true ) + +config_sources = [ + 'pps_config.cc' +] + +dep_docopt = dependency('docopt', required: false) + +if dep_docopt.found() + executable( + 'pps-config', + sources: config_sources, + include_directories: [include_pps, inc_src], + dependencies: [dep_pps, dep_docopt], + cpp_args: '-std=c++17', + install: true + ) +endif diff --git a/src/tool/pps/pps_config.cc b/src/tool/pps/pps_config.cc new file mode 100644 index 00000000000..7d8fb2caeb0 --- /dev/null +++ b/src/tool/pps/pps_config.cc @@ -0,0 +1,227 @@ +/* + * Copyright © 2020-2021 Collabora, Ltd. + * Author: Antonio Caggiano + * + * SPDX-License-Identifier: MIT + */ + +#include + +#include +#include +#include +#include +#include + +#include + +static const char *USAGE = + R"(pps-config + + Usage: + pps-config info + pps-config dump [--gpu=] [--ids=] [--sec=] + pps-config groups [--gpu=] + pps-config counters [--gpu=] + pps-config (-h | --help) + pps-config --version + + Options: + -h --help Show this screen. + --version Show version. + --gpu= GPU number to query [default: 0]. + --ids= Comma separated list of numbers. + --sec= Seconds to wait before dumping performance counters [default: 1]. +)"; + +// Tool running mode +enum class Mode { + // Show help message + Help, + + // Show system information + Info, + + // Show list of available counters + Counters, + + // Groups + Groups, + + // Dump performance counters + Dump, +}; + +std::vector split(const std::string &list, const std::string &separator) +{ + std::vector ret; + std::string_view list_view = list; + while (!list_view.empty()) { + size_t pos = list_view.find(separator); + if (pos == std::string::npos) { + ret.push_back(list_view); + break; + } + ret.push_back(list_view.substr(0, pos)); + list_view = list_view.substr(pos + separator.length(), list_view.length()); + } + return ret; +} + +std::optional to_counter_id(const std::string_view &view) +{ + uint32_t counter_id = 0; + + auto res = std::from_chars(view.data(), view.data() + view.size(), counter_id); + if (res.ec == std::errc::invalid_argument) { + return std::nullopt; + } + + return counter_id; +} + +int main(int argc, const char **argv) +{ + using namespace pps; + + Mode mode = Mode::Help; + auto secs = std::chrono::seconds(1); + uint32_t gpu_num = 0; + std::vector counter_ids; + + auto args = + docopt::docopt(USAGE, {std::next(argv), std::next(argv, argc)}, true, "pps-config 0.3"); + + if (args["info"].asBool()) { + mode = Mode::Info; + } + + if (args["dump"].asBool()) { + mode = Mode::Dump; + } + + if (args["--gpu"]) { + gpu_num = static_cast(args["--gpu"].asLong()); + } + + if (args["--ids"]) { + auto comma_separated_list = args["--ids"].asString(); + std::vector ids_list = split(comma_separated_list, ","); + + for (auto &id : ids_list) { + if (auto counter_id = to_counter_id(id)) { + counter_ids.push_back(*counter_id); + } else { + fprintf(stderr, "Failed to parse counter ids: %s\n", comma_separated_list.c_str()); + return EXIT_FAILURE; + } + } + } + + if (args["--sec"]) { + secs = std::chrono::seconds(args["--sec"].asLong()); + } + + if (args["groups"].asBool()) { + mode = Mode::Groups; + } + + if (args["counters"].asBool()) { + mode = Mode::Counters; + } + + // Docopt shows the help message for us + if (mode == Mode::Help) { + return EXIT_SUCCESS; + } + + switch (mode) { + default: + break; + case Mode::Info: { + // Header: device name, and whether it is supported or not + printf("#%4s %16s %16s\n", "num", "device", "support"); + + auto devices = DrmDevice::create_all(); + for (auto &device : devices) { + auto gpu_num = device.gpu_num; + auto name = device.name; + auto driver = Driver::get_driver(std::move(device)); + printf(" %4u %16s %16s\n", gpu_num, name.c_str(), driver ? "yes" : "no"); + } + + break; + } + case Mode::Dump: { + if (auto device = DrmDevice::create(gpu_num)) { + if (auto driver = Driver::get_driver(std::move(device.value()))) { + driver->init_perfcnt(); + + // Enable counters + if (counter_ids.empty()) { + driver->enable_all_counters(); + } else { + for (auto id : counter_ids) { + driver->enable_counter(id); + } + } + + driver->enable_perfcnt(std::chrono::nanoseconds(secs).count()); + std::this_thread::sleep_for(std::chrono::seconds(secs)); + + // Try dumping until it succeeds + while (!driver->dump_perfcnt()) + ; + // Try collecting samples until it succeeds + while (!driver->next()) + ; + + printf("#%32s %32s\n", "counter", "value"); + for (auto &counter : driver->enabled_counters) { + printf(" %32s ", counter.name.c_str()); + auto value = counter.get_value(*driver); + if (auto d_val = std::get_if(&value)) { + printf("%32f\n", *d_val); + } else if (auto i_val = std::get_if(&value)) + printf("%32li\n", *i_val); + else { + printf("%32s\n", "error"); + } + } + } + } + break; + } + case Mode::Groups: { + if (auto device = DrmDevice::create(gpu_num)) { + if (auto driver = Driver::get_driver(std::move(device.value()))) { + driver->init_perfcnt(); + printf("#%4s %32s\n", "id", "name"); + + for (auto &group : driver->groups) { + printf(" %4u %32s\n", group.id, group.name.c_str()); + } + } + } + + break; + } + case Mode::Counters: { + if (auto device = DrmDevice::create(gpu_num)) { + if (auto driver = Driver::get_driver(std::move(device.value()))) { + driver->init_perfcnt(); + printf("#%4s %32s\n", "id", "name"); + + for (uint32_t i = 0; i < driver->counters.size(); ++i) { + auto &counter = driver->counters[i]; + printf(" %4u %32s\n", counter.id, counter.name.c_str()); + } + } + } + + break; + } + } // switch + + return EXIT_SUCCESS; +}