xref: /dpdk/usertools/dpdk-telemetry.py (revision 03ab51eafda992874a48c392ca66ffb577fe2b71)
1#! /usr/bin/env python3
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2020 Intel Corporation
4
5"""
6Script to be used with V2 Telemetry.
7Allows the user input commands and read the Telemetry response.
8"""
9
10import socket
11import os
12import sys
13import json
14import errno
15import readline
16import argparse
17
18# global vars
19TELEMETRY_VERSION = "v2"
20CMDS = []
21
22
23def read_socket(sock, buf_len, echo=True):
24    """ Read data from socket and return it in JSON format """
25    reply = sock.recv(buf_len).decode()
26    try:
27        ret = json.loads(reply)
28    except json.JSONDecodeError:
29        print("Error in reply: ", reply)
30        sock.close()
31        raise
32    if echo:
33        print(json.dumps(ret))
34    return ret
35
36
37def get_app_name(pid):
38    """ return the app name for a given PID, for printing """
39    proc_cmdline = os.path.join('/proc', str(pid), 'cmdline')
40    try:
41        with open(proc_cmdline) as f:
42            argv0 = f.read(1024).split('\0')[0]
43            return os.path.basename(argv0)
44    except IOError as e:
45        # ignore file not found errors
46        if e.errno != errno.ENOENT:
47            raise
48    return None
49
50
51def handle_socket(path):
52    """ Connect to socket and handle user input """
53    prompt = ''  # this evaluates to false in conditions
54    sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
55    global CMDS
56
57    if os.isatty(sys.stdin.fileno()):
58        prompt = '--> '
59        print("Connecting to " + path)
60    try:
61        sock.connect(path)
62    except OSError:
63        print("Error connecting to " + path)
64        sock.close()
65        return
66    json_reply = read_socket(sock, 1024, prompt)
67    output_buf_len = json_reply["max_output_len"]
68    app_name = get_app_name(json_reply["pid"])
69    if app_name and prompt:
70        print('Connected to application: "%s"' % app_name)
71
72    # get list of commands for readline completion
73    sock.send("/".encode())
74    CMDS = read_socket(sock, output_buf_len, False)["/"]
75
76    # interactive prompt
77    try:
78        text = input(prompt).strip()
79        while text != "quit":
80            if text.startswith('/'):
81                sock.send(text.encode())
82                read_socket(sock, output_buf_len)
83            text = input(prompt).strip()
84    except EOFError:
85        pass
86    finally:
87        sock.close()
88
89
90def readline_complete(text, state):
91    """ Find any matching commands from the list based on user input """
92    all_cmds = ['quit'] + CMDS
93    if text:
94        matches = [c for c in all_cmds if c.startswith(text)]
95    else:
96        matches = all_cmds
97    return matches[state]
98
99
100def get_dpdk_runtime_dir(fp):
101    """ Using the same logic as in DPDK's EAL, get the DPDK runtime directory
102    based on the file-prefix and user """
103    if (os.getuid() == 0):
104        return os.path.join('/var/run/dpdk', fp)
105    return os.path.join(os.environ.get('XDG_RUNTIME_DIR', '/tmp'), 'dpdk', fp)
106
107
108readline.parse_and_bind('tab: complete')
109readline.set_completer(readline_complete)
110readline.set_completer_delims(readline.get_completer_delims().replace('/', ''))
111
112parser = argparse.ArgumentParser()
113parser.add_argument('-f', '--file-prefix', default='rte',
114                    help='Provide file-prefix for DPDK runtime directory')
115args = parser.parse_args()
116rd = get_dpdk_runtime_dir(args.file_prefix)
117handle_socket(os.path.join(rd, 'dpdk_telemetry.{}'.format(TELEMETRY_VERSION)))
118