• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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