From 56ccea58ae7f6fd56cf4a1697d2cceb68866b552 Mon Sep 17 00:00:00 2001 From: Rafael Antognolli Date: Thu, 12 Dec 2019 06:45:51 -0800 Subject: [PATCH] vulkan/overlay: Add basic overlay control script. This can be used to start/stop statistics capturing from the command line. v3: - Install script (Lionel) Reviewed-by: Lionel Landwerlin --- .../overlay-layer/mesa-overlay-control.py | 203 ++++++++++++++++++ src/vulkan/overlay-layer/meson.build | 7 + 2 files changed, 210 insertions(+) create mode 100755 src/vulkan/overlay-layer/mesa-overlay-control.py diff --git a/src/vulkan/overlay-layer/mesa-overlay-control.py b/src/vulkan/overlay-layer/mesa-overlay-control.py new file mode 100755 index 00000000000..6947250cff8 --- /dev/null +++ b/src/vulkan/overlay-layer/mesa-overlay-control.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +import os +import socket +import sys +import select +from select import EPOLLIN, EPOLLPRI, EPOLLERR +import time +from collections import namedtuple +import argparse + +TIMEOUT = 1.0 # seconds + +VERSION_HEADER = bytearray('MesaOverlayControlVersion', 'utf-8') +DEVICE_NAME_HEADER = bytearray('DeviceName', 'utf-8') +MESA_VERSION_HEADER = bytearray('MesaVersion', 'utf-8') + +DEFAULT_SERVER_ADDRESS = "\0mesa_overlay" + +class Connection: + def __init__(self, path): + # Create a Unix Domain socket and connect + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + try: + sock.connect(path) + except socket.error as msg: + print(msg) + sys.exit(1) + + self.sock = sock + + # initialize poll interface and register socket + epoll = select.epoll() + epoll.register(sock, EPOLLIN | EPOLLPRI | EPOLLERR) + self.epoll = epoll + + def recv(self, timeout): + ''' + timeout as float in seconds + returns: + - None on error or disconnection + - bytes() (empty) on timeout + ''' + + events = self.epoll.poll(timeout) + for ev in events: + (fd, event) = ev + if fd != self.sock.fileno(): + continue + + # check for socket error + if event & EPOLLERR: + return None + + # EPOLLIN or EPOLLPRI, just read the message + msg = self.sock.recv(4096) + + # socket disconnected + if len(msg) == 0: + return None + + return msg + + return bytes() + + def send(self, msg): + self.sock.send(msg) + +class MsgParser: + MSGBEGIN = bytes(':', 'utf-8')[0] + MSGEND = bytes(';', 'utf-8')[0] + MSGSEP = bytes('=', 'utf-8')[0] + + def __init__(self, conn): + self.cmdpos = 0 + self.parampos = 0 + self.bufferpos = 0 + self.reading_cmd = False + self.reading_param = False + self.buffer = None + self.cmd = bytearray(4096) + self.param = bytearray(4096) + + self.conn = conn + + def readCmd(self, ncmds, timeout=TIMEOUT): + ''' + returns: + - None on error or disconnection + - bytes() (empty) on timeout + ''' + + parsed = [] + + remaining = timeout + + while remaining > 0 and ncmds > 0: + now = time.monotonic() + + if self.buffer == None: + self.buffer = self.conn.recv(remaining) + self.bufferpos = 0 + + # disconnected or error + if self.buffer == None: + return None + + for i in range(self.bufferpos, len(self.buffer)): + c = self.buffer[i] + self.bufferpos += 1 + if c == self.MSGBEGIN: + self.cmdpos = 0 + self.parampos = 0 + self.reading_cmd = True + self.reading_param = False + elif c == self.MSGEND: + if not self.reading_cmd: + continue + self.reading_cmd = False + self.reading_param = False + + cmd = self.cmd[0:self.cmdpos] + param = self.param[0:self.parampos] + self.reading_cmd = False + self.reading_param = False + + parsed.append((cmd, param)) + ncmds -= 1 + if ncmds == 0: + break + elif c == self.MSGSEP: + if self.reading_cmd: + self.reading_param = True + else: + if self.reading_param: + self.param[self.parampos] = c + self.parampos += 1 + elif self.reading_cmd: + self.cmd[self.cmdpos] = c + self.cmdpos += 1 + + # if we read the entire buffer and didn't finish the command, + # throw it away + self.buffer = None + + # check if we have time for another iteration + elapsed = time.monotonic() - now + remaining = max(0, remaining - elapsed) + + # timeout + return parsed + +def control(args): + if args.socket: + address = '\0' + args.socket + else: + address = DEFAULT_SERVER_ADDRESS + + conn = Connection(address) + msgparser = MsgParser(conn) + + version = None + name = None + mesa_version = None + + msgs = msgparser.readCmd(3) + + for m in msgs: + cmd, param = m + if cmd == VERSION_HEADER: + version = int(param) + elif cmd == DEVICE_NAME_HEADER: + name = param.decode('utf-8') + elif cmd == MESA_VERSION_HEADER: + mesa_version = param.decode('utf-8') + + if version != 1 or name == None or mesa_version == None: + print('ERROR: invalid protocol') + sys.exit(1) + + + if args.info: + info = "Protocol Version: {}\n" + info += "Device Name: {}\n" + info += "Mesa Version: {}" + print(info.format(version, name, mesa_version)) + + if args.cmd == 'start-capture': + conn.send(bytearray(':capture=1;', 'utf-8')) + elif args.cmd == 'stop-capture': + conn.send(bytearray(':capture=0;', 'utf-8')) + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='MESA_overlay control client') + parser.add_argument('--info', action='store_true', help='Print info from socket') + parser.add_argument('--socket', '-s', type=str, help='Path to socket') + + commands = parser.add_subparsers(help='commands to run', dest='cmd') + commands.add_parser('start-capture') + commands.add_parser('stop-capture') + + args = parser.parse_args() + + control(args) diff --git a/src/vulkan/overlay-layer/meson.build b/src/vulkan/overlay-layer/meson.build index 96134d356ff..5b553d0b51e 100644 --- a/src/vulkan/overlay-layer/meson.build +++ b/src/vulkan/overlay-layer/meson.build @@ -51,3 +51,10 @@ install_data( files('VkLayer_MESA_overlay.json'), install_dir : join_paths(get_option('datadir'), 'vulkan', 'explicit_layer.d'), ) + +configure_file( + input : files('mesa-overlay-control.py'), + output : '@PLAINNAME@', + configuration : configuration_data(), # only copy the file + install_dir: get_option('bindir'), +) -- 2.30.2