diff options
author | Lucas Stach <l.stach@pengutronix.de> | 2018-06-12 18:23:59 +0200 |
---|---|---|
committer | Lucas Stach <l.stach@pengutronix.de> | 2018-06-13 11:58:00 +0200 |
commit | ba8b97cf53449ba22ec319901e9ba46fd75d066e (patch) | |
tree | 51a6c7fb7225a7f5fa32e810c5a6425e78f98f9b | |
parent | 0fd514592cae47f65ad8dd454c807ce9f4297fc6 (diff) | |
download | drm-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.c | 232 |
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; +} |