DPDK patches and discussions
 help / color / mirror / Atom feed
From: Bruce Richardson <bruce.richardson@intel.com>
To: dev@dpdk.org
Cc: Bruce Richardson <bruce.richardson@intel.com>
Subject: [RFC PATCH 1/7] usertools: add new script to monitor telemetry on terminal
Date: Wed, 10 Dec 2025 16:55:26 +0000	[thread overview]
Message-ID: <20251210165532.103450-2-bruce.richardson@intel.com> (raw)
In-Reply-To: <20251210165532.103450-1-bruce.richardson@intel.com>

The dpdk-telemetry.py script is useful for getting telemetry
interactively, but sometimes we want to monitor stats over time, so add
a telemetry-watcher script to do so.

Start off such a script with basic arg processing, and connecting to
dpdk-telemetry as a subprocess, so that we can send-receive commands
from it.

Signed-off-by: Bruce Richardson <bruce.richardson@intel.com>
---
 usertools/dpdk-telemetry-watcher.py | 200 ++++++++++++++++++++++++++++
 usertools/meson.build               |   1 +
 2 files changed, 201 insertions(+)
 create mode 100755 usertools/dpdk-telemetry-watcher.py

diff --git a/usertools/dpdk-telemetry-watcher.py b/usertools/dpdk-telemetry-watcher.py
new file mode 100755
index 0000000000..01c8683d33
--- /dev/null
+++ b/usertools/dpdk-telemetry-watcher.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2025 Intel Corporation
+
+"""
+Script to monitor DPDK telemetry statistics on the command line.
+Wraps dpdk-telemetry.py to provide continuous monitoring capabilities.
+"""
+
+import argparse
+import subprocess
+import sys
+import os
+import shutil
+import errno
+import json
+
+
+def get_app_name(pid):
+    """return the app name for a given PID, for printing"""
+    proc_cmdline = os.path.join("/proc", str(pid), "cmdline")
+    try:
+        with open(proc_cmdline) as f:
+            argv0 = f.read(1024).split("\0")[0]
+            return os.path.basename(argv0)
+    except IOError as e:
+        # ignore file not found errors
+        if e.errno != errno.ENOENT:
+            raise
+    return None
+
+
+def find_telemetry_script():
+    """Find the dpdk-telemetry.py script in the script directory or PATH.
+
+    Returns:
+        str: Path to the dpdk-telemetry.py script
+
+    Exits:
+        If the script cannot be found
+    """
+    # First, try to find it in the same directory as this script
+    script_dir = os.path.dirname(os.path.abspath(__file__))
+    telemetry_script = os.path.join(script_dir, "dpdk-telemetry.py")
+
+    # If not found locally, check if it's in PATH
+    if not os.path.exists(telemetry_script):
+        telemetry_in_path = shutil.which("dpdk-telemetry.py")
+        if telemetry_in_path:
+            telemetry_script = telemetry_in_path
+        else:
+            print(
+                "Error: dpdk-telemetry.py not found in script directory or PATH",
+                file=sys.stderr,
+            )
+            sys.exit(1)
+
+    return telemetry_script
+
+
+def create_telemetry_process(telemetry_script, args_list):
+    """Create a subprocess for dpdk-telemetry.py with pipes.
+
+    Args:
+        telemetry_script: Path to the dpdk-telemetry.py script
+        args_list: List of arguments to pass to the script
+
+    Returns:
+        subprocess.Popen: Process handle with stdin/stdout/stderr pipes
+
+    Exits:
+        If the process cannot be created
+    """
+    # Build the command
+    cmd = [sys.executable, telemetry_script] + args_list
+
+    try:
+        process = subprocess.Popen(
+            cmd,
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=True,
+            bufsize=1,  # Line buffered
+        )
+        return process
+    except FileNotFoundError:
+        print(f"Error: Python interpreter or script not found", file=sys.stderr)
+        sys.exit(1)
+    except Exception as e:
+        print(f"Error running dpdk-telemetry.py: {e}", file=sys.stderr)
+        sys.exit(1)
+
+
+def query_telemetry(process, command):
+    """Send a telemetry command and return the parsed JSON response.
+
+    Args:
+        process: The subprocess.Popen handle to the telemetry process
+        command: The telemetry command to send (e.g., "/info" or "/ethdev/stats,0")
+
+    Returns:
+        dict: The parsed JSON response with the command wrapper stripped,
+              or None if there was an error
+    """
+    # Send the command
+    process.stdin.write(f"{command}\n")
+    process.stdin.flush()
+
+    # Read the JSON response
+    response = process.stdout.readline()
+    try:
+        data = json.loads(response)
+        # When run non-interactively, the response is wrapped with the command
+        # e.g., {"/info": {"version": ..., "pid": ...}}
+        # or {"/ethdev/stats,0": {...}}
+        # The response should have exactly one key which is the command
+        if len(data) == 1:
+            # Extract the value, ignoring the key
+            return next(iter(data.values()))
+        else:
+            return data
+    except (json.JSONDecodeError, KeyError):
+        return None
+
+
+def print_connected_app(process):
+    """Query and print the name of the connected DPDK application.
+
+    Args:
+        process: The subprocess.Popen handle to the telemetry process
+    """
+    info = query_telemetry(process, "/info")
+    if info and "pid" in info:
+        app_name = get_app_name(info["pid"])
+        if app_name:
+            print(f'Connected to application: "{app_name}"')
+
+
+def main():
+    """Main function to parse arguments and run dpdk-telemetry.py with a pipe"""
+
+    # Parse command line arguments - matching dpdk-telemetry.py parameters
+    parser = argparse.ArgumentParser(
+        description="Monitor DPDK telemetry statistics on the command line"
+    )
+    parser.add_argument(
+        "-f",
+        "--file-prefix",
+        default="rte",
+        help="Provide file-prefix for DPDK runtime directory",
+    )
+    parser.add_argument(
+        "-i",
+        "--instance",
+        default=0,
+        type=int,
+        help="Provide instance number for DPDK application",
+    )
+    parser.add_argument(
+        "-l",
+        "--list",
+        action="store_true",
+        default=False,
+        help="List all possible file-prefixes and exit",
+    )
+
+    args = parser.parse_args()
+
+    # Find the dpdk-telemetry.py script
+    telemetry_script = find_telemetry_script()
+
+    # Build arguments list
+    args_list = ["-f", args.file_prefix, "-i", str(args.instance)]
+
+    if args.list:
+        args_list.append("-l")
+        # For --list, just run the command directly without pipes
+        cmd = [sys.executable, telemetry_script] + args_list
+        return subprocess.run(cmd).returncode
+
+    # Run dpdk-telemetry.py with pipes for stdin and stdout
+    process = create_telemetry_process(telemetry_script, args_list)
+
+    # Get and display the connected application name
+    print_connected_app(process)
+
+    # TODO: Add monitoring logic here
+
+    # Clean up
+    process.stdin.close()
+    process.stdout.close()
+    process.stderr.close()
+    process.wait()
+
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/usertools/meson.build b/usertools/meson.build
index eb48e2f440..114d0a65b4 100644
--- a/usertools/meson.build
+++ b/usertools/meson.build
@@ -12,6 +12,7 @@ install_data([
             'dpdk-hugepages.py',
             'dpdk-rss-flows.py',
             'dpdk-telemetry-exporter.py',
+            'dpdk-telemetry-watcher.py',
         ],
         install_dir: 'bin')
 
-- 
2.51.0


  reply	other threads:[~2025-12-10 16:55 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-10 16:55 [RFC PATCH 0/7] Add script for real-time telemetry monitoring Bruce Richardson
2025-12-10 16:55 ` Bruce Richardson [this message]
2025-12-10 16:55 ` [RFC PATCH 2/7] usertools/telemetry-watcher: add displaying stats Bruce Richardson
2025-12-10 16:55 ` [RFC PATCH 3/7] usertools/telemetry-watcher: add delta and timeout opts Bruce Richardson
2025-12-10 16:55 ` [RFC PATCH 4/7] usertools/telemetry-watcher: add total and one-line opts Bruce Richardson
2025-12-10 16:55 ` [RFC PATCH 5/7] usertools/telemetry-watcher: add thousands separator Bruce Richardson
2025-12-10 16:55 ` [RFC PATCH 6/7] usertools/telemetry-watcher: add eth name shortcuts Bruce Richardson
2025-12-10 16:55 ` [RFC PATCH 7/7] usertools/telemetry-watcher: support reconnection Bruce Richardson
2025-12-11  1:09 ` [RFC PATCH 0/7] Add script for real-time telemetry monitoring Stephen Hemminger
2025-12-11  9:10   ` Bruce Richardson
2025-12-12  5:32 ` Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251210165532.103450-2-bruce.richardson@intel.com \
    --to=bruce.richardson@intel.com \
    --cc=dev@dpdk.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).