summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucas Stach <l.stach@pengutronix.de>2018-06-12 18:23:59 +0200
committerLucas Stach <l.stach@pengutronix.de>2018-06-13 11:58:00 +0200
commitba8b97cf53449ba22ec319901e9ba46fd75d066e (patch)
tree51a6c7fb7225a7f5fa32e810c5a6425e78f98f9b
parent0fd514592cae47f65ad8dd454c807ce9f4297fc6 (diff)
downloaddrm-sched-top-ba8b97cf53449ba22ec319901e9ba46fd75d066e.tar.gz
drm-sched-top-ba8b97cf53449ba22ec319901e9ba46fd75d066e.tar.xz
add some basic functionality
Signed-off-by: Lucas Stach <l.stach@pengutronix.de>
-rw-r--r--drm-sched-top.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/drm-sched-top.c b/drm-sched-top.c
index bdcccd3..f599021 100644
--- a/drm-sched-top.c
+++ b/drm-sched-top.c
@@ -20,3 +20,235 @@
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MAX_RINGS 256
+
+struct ring {
+ const char *name;
+ int active_time_fd;
+ uint64_t last_active_time;
+ long last_query_time;
+};
+
+struct rings {
+ int num_rings;
+ struct ring *rings[MAX_RINGS];
+};
+
+static int
+filename_to_buf(const int fd, char *buf, unsigned int bufsize)
+{
+ int err;
+ ssize_t ret;
+
+ ret = pread(fd, buf, bufsize - 1, 0);
+ err = errno;
+ if (ret < 1) {
+ errno = ret < 0 ? err : ENOMSG;
+
+ return -1;
+ }
+
+ if (ret > 1 && buf[ret - 1] == '\n')
+ buf[ret - 1] = '\0';
+ else
+ buf[ret] = '\0';
+
+ return 0;
+}
+
+static uint64_t fd_to_u64(const int fd, int base)
+{
+ char buf[64], *b;
+
+ if (filename_to_buf(fd, buf, sizeof(buf)))
+ return 0;
+
+ /*
+ * Handle both single integer and key=value formats by skipping
+ * leading non-digits.
+ */
+ b = buf;
+ while (*b && !isdigit(*b))
+ b++;
+
+ return strtoull(b, NULL, base);
+}
+
+static const char *bars[] = { " ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█" };
+
+static void
+print_percentage_bar(double percent, int max_len)
+{
+ int bar_len = percent * (8 * (max_len - 2)) / 100.0;
+ int i;
+
+ printf("|\e[1;33m");
+
+ for (i = bar_len; i >= 8; i -= 8)
+ printf("%s", bars[8]);
+ if (i)
+ printf("%s", bars[i]);
+
+ for (i = 0; i < (max_len - 2 - (bar_len + 7) / 8); i++)
+ putchar(' ');
+
+ printf("\e[0m|");
+}
+
+static struct rings *discover_rings(void)
+{
+ struct rings *ringss;
+ struct dirent *dent;
+ DIR *ring_dir;
+
+ ringss = malloc(sizeof(*ringss));
+ if (!ringss)
+ return NULL;
+ memset(ringss, 0, sizeof(*ringss));
+
+ ring_dir = opendir("/sys/class/drm/scheduler/rings");
+ if (!ring_dir)
+ return NULL;
+
+ while ((dent = readdir(ring_dir)) != NULL) {
+ char buf[1024];
+ struct ring *ring;
+
+ if (ringss->num_rings >= MAX_RINGS)
+ break;
+
+ if (!strncmp(dent->d_name, ".", 1))
+ continue;
+
+ ring = malloc(sizeof(*ring));
+ if (!ring)
+ break;
+ memset(ring, 0, sizeof(*ring));
+
+ ring->name = strdup(dent->d_name);
+
+ snprintf(buf, 1024,
+ "/sys/class/drm/scheduler/rings/%s/active_us",
+ dent->d_name);
+ ring->active_time_fd = open(buf, O_RDONLY);
+ if (ring->active_time_fd < 0) {
+ free(ring);
+ break;
+ }
+
+ ringss->rings[ringss->num_rings] = ring;
+ ringss->num_rings++;
+ }
+
+ closedir(ring_dir);
+
+ return ringss;
+}
+
+static void usage(const char *appname)
+{
+ printf("drm-sched-top - Display a top like usage of DRM scheduler driven GPUs\n"
+ "\n"
+ "Usage: %s [parameters]\n"
+ "\n"
+ "\tThe following parameters are optional:\n\n"
+ "\t[-s <ms>] Refresh period in milliseconds (default 1000).\n"
+ "\t[-h] Show this help text.\n"
+ "\n",
+ appname);
+}
+
+int main(int argc, char **argv)
+{
+ unsigned int period_us = 1000000;
+ struct rings *ringss;
+ DIR *sched_dir;
+ int ch;
+
+ /* Parse options */
+ while ((ch = getopt(argc, argv, "s:h")) != -1) {
+ switch (ch) {
+ case 's':
+ period_us = atoi(optarg) * 1000;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ default:
+ fprintf(stderr, "Invalid option %c!\n", (char)optopt);
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+
+ /* Sanity check before trying to do anything else */
+ sched_dir = opendir("/sys/class/drm/scheduler");
+ if (!sched_dir) {
+ fprintf(stderr, "could not open scheduler statistics directory\n");
+ return -1;
+ }
+
+ closedir(sched_dir);
+
+ ringss = discover_rings();
+ if (!ringss) {
+ fprintf(stderr, "could not find any engines\n");
+ exit(1);
+ }
+
+ for (;;) {
+ struct winsize ws;
+ int i;
+
+
+ ioctl(0, TIOCGWINSZ, &ws);
+
+ printf("\033[H\033[J");
+ printf("Engines:\n");
+
+ for (i = 0; i < ringss->num_rings; i++) {
+ struct ring *ring = ringss->rings[i];
+ uint64_t active_time, query_timestamp;
+ double active_percent;
+ struct timespec time;
+ int len = 0;
+
+ active_time = fd_to_u64(ring->active_time_fd, 10);
+ clock_gettime(CLOCK_MONOTONIC, &time);
+ query_timestamp = time.tv_sec * 1000000 + time.tv_nsec / 1000;
+
+ active_percent = (active_time - ring->last_active_time) * 100.0;
+ active_percent /= (query_timestamp - ring->last_query_time);
+
+ len += printf(" %s", ring->name);
+ while(len < 24)
+ len += printf(" ");
+
+ print_percentage_bar(active_percent, ws.ws_col - len - 10);
+ len += printf(" %3.2f %%", active_percent);
+ printf("\n");
+
+ ring->last_active_time = active_time;
+ ring->last_query_time = query_timestamp;
+ }
+
+ usleep(period_us);
+ }
+
+ return 0;
+}