Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f9ff1087 authored by Stefan Raspl's avatar Stefan Raspl Committed by Paolo Bonzini
Browse files

tools/kvm_stat: add option '--guest'



Add a new option '-g'/'--guest' to select a particular process by providing
the QEMU guest name.
Notes:
- The logic to figure out the pid corresponding to the guest name might look
  scary, but works pretty reliably in practice; in the unlikely event that it
  returns add'l flukes, it will bail out and hint at using '-p' instead, no
  harm done.
- Mixing '-g' and '-p' is possible, and the final instance specified on the
  command line is the significant one. This is consistent with current
  behavior for '-p' which, if specified multiple times, also regards the final
  instance as the significant one.

Signed-off-by: default avatarStefan Raspl <raspl@linux.vnet.ibm.com>
Reviewed-by: default avatarJanosch Frank <frankja@linux.vnet.ibm.com>
Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
parent 645c1728
Loading
Loading
Loading
Loading
+99 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import fcntl
import resource
import struct
import re
import subprocess
from collections import defaultdict

VMX_EXIT_REASONS = {
@@ -320,6 +321,30 @@ def parse_int_list(list_string):
    return integers


def get_pid_from_gname(gname):
    """Fuzzy function to convert guest name to QEMU process pid.

    Returns a list of potential pids, can be empty if no match found.
    Throws an exception on processing errors.

    """
    pids = []
    try:
        child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'],
                                 stdout=subprocess.PIPE)
    except:
        raise Exception
    for line in child.stdout:
        line = line.lstrip().split(' ', 1)
        # perform a sanity check before calling the more expensive
        # function to possibly extract the guest name
        if ' -name ' in line[1] and gname == get_gname_from_pid(line[0]):
            pids.append(int(line[0]))
    child.stdout.close()

    return pids


def get_gname_from_pid(pid):
    """Returns the guest name for a QEMU process pid.

@@ -977,7 +1002,7 @@ class Tui(object):
            except re.error:
                continue

    def show_vm_selection(self):
    def show_vm_selection_by_pid(self):
        """Draws PID selection mask.

        Asks for a pid until a valid pid or 0 has been entered.
@@ -1016,6 +1041,50 @@ class Tui(object):
                msg = '"' + str(pid) + '": Not a valid pid'
                continue

    def show_vm_selection_by_guest_name(self):
        """Draws guest selection mask.

        Asks for a guest name until a valid guest name or '' is entered.

        """
        msg = ''
        while True:
            self.screen.erase()
            self.screen.addstr(0, 0,
                               'Show statistics for specific guest.',
                               curses.A_BOLD)
            self.screen.addstr(1, 0,
                               'This might limit the shown data to the trace '
                               'statistics.')
            self.screen.addstr(5, 0, msg)
            curses.echo()
            self.screen.addstr(3, 0, "Guest [ENTER or guest]: ")
            gname = self.screen.getstr()
            curses.noecho()

            if not gname:
                self.refresh_header(0)
                self.update_pid(0)
                break
            else:
                pids = []
                try:
                    pids = get_pid_from_gname(gname)
                except:
                    msg = '"' + gname + '": Internal error while searching, ' \
                          'use pid filter instead'
                    continue
                if len(pids) == 0:
                    msg = '"' + gname + '": Not an active guest'
                    continue
                if len(pids) > 1:
                    msg = '"' + gname + '": Multiple matches found, use pid ' \
                          'filter instead'
                    continue
                self.refresh_header(pids[0])
                self.update_pid(pids[0])
                break

    def show_stats(self):
        """Refreshes the screen and processes user input."""
        sleeptime = DELAY_INITIAL
@@ -1035,8 +1104,11 @@ class Tui(object):
                if char == 'f':
                    self.show_filter_selection()
                    sleeptime = DELAY_INITIAL
                if char == 'g':
                    self.show_vm_selection_by_guest_name()
                    sleeptime = DELAY_INITIAL
                if char == 'p':
                    self.show_vm_selection()
                    self.show_vm_selection_by_pid()
                    sleeptime = DELAY_INITIAL
            except KeyboardInterrupt:
                break
@@ -1106,6 +1178,7 @@ Requirements:

Interactive Commands:
   f     filter by regular expression
   g     filter by guest name
   p     filter by PID
   q     quit
   x     toggle reporting of stats for individual child trace events
@@ -1119,6 +1192,22 @@ Press any other key to refresh statistics immediately.
            else:
                return ""

    def cb_guest_to_pid(option, opt, val, parser):
        try:
            pids = get_pid_from_gname(val)
        except:
            raise optparse.OptionValueError('Error while searching for guest '
                                            '"{}", use "-p" to specify a pid '
                                            'instead'.format(val))
        if len(pids) == 0:
            raise optparse.OptionValueError('No guest by the name "{}" '
                                            'found'.format(val))
        if len(pids) > 1:
            raise optparse.OptionValueError('Multiple processes found (pids: '
                                            '{}) - use "-p" to specify a pid '
                                            'instead'.format(" ".join(pids)))
        parser.values.pid = pids[0]

    optparser = optparse.OptionParser(description=description_text,
                                      formatter=PlainHelpFormatter())
    optparser.add_option('-1', '--once', '--batch',
@@ -1158,6 +1247,14 @@ Press any other key to refresh statistics immediately.
                         dest='pid',
                         help='restrict statistics to pid',
                         )
    optparser.add_option('-g', '--guest',
                         action='callback',
                         type='string',
                         dest='pid',
                         metavar='GUEST',
                         help='restrict statistics to guest by name',
                         callback=cb_guest_to_pid,
                         )
    (options, _) = optparser.parse_args(sys.argv)
    return options

+6 −0
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ INTERACTIVE COMMANDS
[horizontal]
*f*::	filter by regular expression

*g*::	filter by guest name

*p*::	filter by PID

*q*::	quit
@@ -62,6 +64,10 @@ OPTIONS
--pid=<pid>::
	limit statistics to one virtual machine (pid)

-g<guest>::
--guest=<guest_name>::
	limit statistics to one virtual machine (guest name)

-f<fields>::
--fields=<fields>::
	fields to display (regex)