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
18 static 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
38 enum 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
split(const std::string & list,const std::string & separator)55 std::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
to_counter_id(const std::string_view & view)71 std::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
main(int argc,const char ** argv)83 int 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