1/* 2 * Copyright © 2020-2021 Collabora, Ltd. 3 * Author: Antonio Caggiano <antonio.caggiano@collabora.com> 4 * 5 * SPDX-License-Identifier: MIT 6 */ 7 8#include <pps/pps_driver.h> 9 10#include <charconv> 11#include <cstdlib> 12#include <cstring> 13#include <optional> 14#include <thread> 15 16#include <docopt/docopt.h> 17 18static const char *USAGE = 19 R"(pps-config 20 21 Usage: 22 pps-config info 23 pps-config dump [--gpu=<n>] [--ids=<n>] [--sec=<n>] 24 pps-config groups [--gpu=<n>] 25 pps-config counters [--gpu=<n>] 26 pps-config (-h | --help) 27 pps-config --version 28 29 Options: 30 -h --help Show this screen. 31 --version Show version. 32 --gpu=<n> GPU number to query [default: 0]. 33 --ids=<n> Comma separated list of numbers. 34 --sec=<n> Seconds to wait before dumping performance counters [default: 1]. 35)"; 36 37// Tool running mode 38enum class Mode { 39 // Show help message 40 Help, 41 42 // Show system information 43 Info, 44 45 // Show list of available counters 46 Counters, 47 48 // Groups 49 Groups, 50 51 // Dump performance counters 52 Dump, 53}; 54 55std::vector<std::string_view> split(const std::string &list, const std::string &separator) 56{ 57 std::vector<std::string_view> ret; 58 std::string_view list_view = list; 59 while (!list_view.empty()) { 60 size_t pos = list_view.find(separator); 61 if (pos == std::string::npos) { 62 ret.push_back(list_view); 63 break; 64 } 65 ret.push_back(list_view.substr(0, pos)); 66 list_view = list_view.substr(pos + separator.length(), list_view.length()); 67 } 68 return ret; 69} 70 71std::optional<uint32_t> to_counter_id(const std::string_view &view) 72{ 73 uint32_t counter_id = 0; 74 75 auto res = std::from_chars(view.data(), view.data() + view.size(), counter_id); 76 if (res.ec == std::errc::invalid_argument) { 77 return std::nullopt; 78 } 79 80 return counter_id; 81} 82 83int main(int argc, const char **argv) 84{ 85 using namespace pps; 86 87 Mode mode = Mode::Help; 88 auto secs = std::chrono::seconds(1); 89 uint32_t gpu_num = 0; 90 std::vector<uint32_t> counter_ids; 91 92 auto args = 93 docopt::docopt(USAGE, {std::next(argv), std::next(argv, argc)}, true, "pps-config 0.3"); 94 95 if (args["info"].asBool()) { 96 mode = Mode::Info; 97 } 98 99 if (args["dump"].asBool()) { 100 mode = Mode::Dump; 101 } 102 103 if (args["--gpu"]) { 104 gpu_num = static_cast<uint32_t>(args["--gpu"].asLong()); 105 } 106 107 if (args["--ids"]) { 108 auto comma_separated_list = args["--ids"].asString(); 109 std::vector<std::string_view> ids_list = split(comma_separated_list, ","); 110 111 for (auto &id : ids_list) { 112 if (auto counter_id = to_counter_id(id)) { 113 counter_ids.push_back(*counter_id); 114 } else { 115 fprintf(stderr, "Failed to parse counter ids: %s\n", comma_separated_list.c_str()); 116 return EXIT_FAILURE; 117 } 118 } 119 } 120 121 if (args["--sec"]) { 122 secs = std::chrono::seconds(args["--sec"].asLong()); 123 } 124 125 if (args["groups"].asBool()) { 126 mode = Mode::Groups; 127 } 128 129 if (args["counters"].asBool()) { 130 mode = Mode::Counters; 131 } 132 133 // Docopt shows the help message for us 134 if (mode == Mode::Help) { 135 return EXIT_SUCCESS; 136 } 137 138 switch (mode) { 139 default: 140 break; 141 case Mode::Info: { 142 // Header: device name, and whether it is supported or not 143 printf("#%4s %16s %16s\n", "num", "device", "support"); 144 145 auto devices = DrmDevice::create_all(); 146 for (auto &device : devices) { 147 auto gpu_num = device.gpu_num; 148 auto name = device.name; 149 auto driver = Driver::get_driver(std::move(device)); 150 printf(" %4u %16s %16s\n", gpu_num, name.c_str(), driver ? "yes" : "no"); 151 } 152 153 break; 154 } 155 case Mode::Dump: { 156 if (auto device = DrmDevice::create(gpu_num)) { 157 if (auto driver = Driver::get_driver(std::move(device.value()))) { 158 driver->init_perfcnt(); 159 160 // Enable counters 161 if (counter_ids.empty()) { 162 driver->enable_all_counters(); 163 } else { 164 for (auto id : counter_ids) { 165 driver->enable_counter(id); 166 } 167 } 168 169 driver->enable_perfcnt(std::chrono::nanoseconds(secs).count()); 170 std::this_thread::sleep_for(std::chrono::seconds(secs)); 171 172 // Try dumping until it succeeds 173 while (!driver->dump_perfcnt()) 174 ; 175 // Try collecting samples until it succeeds 176 while (!driver->next()) 177 ; 178 179 printf("#%32s %32s\n", "counter", "value"); 180 for (auto &counter : driver->enabled_counters) { 181 printf(" %32s ", counter.name.c_str()); 182 auto value = counter.get_value(*driver); 183 if (auto d_val = std::get_if<double>(&value)) { 184 printf("%32f\n", *d_val); 185 } else if (auto i_val = std::get_if<int64_t>(&value)) 186 printf("%32li\n", *i_val); 187 else { 188 printf("%32s\n", "error"); 189 } 190 } 191 } 192 } 193 break; 194 } 195 case Mode::Groups: { 196 if (auto device = DrmDevice::create(gpu_num)) { 197 if (auto driver = Driver::get_driver(std::move(device.value()))) { 198 driver->init_perfcnt(); 199 printf("#%4s %32s\n", "id", "name"); 200 201 for (auto &group : driver->groups) { 202 printf(" %4u %32s\n", group.id, group.name.c_str()); 203 } 204 } 205 } 206 207 break; 208 } 209 case Mode::Counters: { 210 if (auto device = DrmDevice::create(gpu_num)) { 211 if (auto driver = Driver::get_driver(std::move(device.value()))) { 212 driver->init_perfcnt(); 213 printf("#%4s %32s\n", "id", "name"); 214 215 for (uint32_t i = 0; i < driver->counters.size(); ++i) { 216 auto &counter = driver->counters[i]; 217 printf(" %4u %32s\n", counter.id, counter.name.c_str()); 218 } 219 } 220 } 221 222 break; 223 } 224 } // switch 225 226 return EXIT_SUCCESS; 227} 228