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