diff options
author | Stefan Raspl <raspl@linux.vnet.ibm.com> | 2017-03-10 07:40:13 -0500 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2017-03-29 06:01:31 -0400 |
commit | f9ff1087354e5e063b96a291360a8de84bea0bed (patch) | |
tree | 906750a31a22bac0619fa4ecd57d2516860c5276 /tools | |
parent | 645c1728a9d33d78028d93a2ed770f51df0a92c6 (diff) |
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: Stefan Raspl <raspl@linux.vnet.ibm.com>
Reviewed-by: Janosch Frank <frankja@linux.vnet.ibm.com>
Signed-off-by: Radim Krčmář <rkrcmar@redhat.com>
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/kvm/kvm_stat/kvm_stat | 101 | ||||
-rw-r--r-- | tools/kvm/kvm_stat/kvm_stat.txt | 6 |
2 files changed, 105 insertions, 2 deletions
diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat index f2a868b696a8..f263312c9a29 100755 --- a/tools/kvm/kvm_stat/kvm_stat +++ b/tools/kvm/kvm_stat/kvm_stat | |||
@@ -30,6 +30,7 @@ import fcntl | |||
30 | import resource | 30 | import resource |
31 | import struct | 31 | import struct |
32 | import re | 32 | import re |
33 | import subprocess | ||
33 | from collections import defaultdict | 34 | from collections import defaultdict |
34 | 35 | ||
35 | VMX_EXIT_REASONS = { | 36 | VMX_EXIT_REASONS = { |
@@ -320,6 +321,30 @@ def parse_int_list(list_string): | |||
320 | return integers | 321 | return integers |
321 | 322 | ||
322 | 323 | ||
324 | def get_pid_from_gname(gname): | ||
325 | """Fuzzy function to convert guest name to QEMU process pid. | ||
326 | |||
327 | Returns a list of potential pids, can be empty if no match found. | ||
328 | Throws an exception on processing errors. | ||
329 | |||
330 | """ | ||
331 | pids = [] | ||
332 | try: | ||
333 | child = subprocess.Popen(['ps', '-A', '--format', 'pid,args'], | ||
334 | stdout=subprocess.PIPE) | ||
335 | except: | ||
336 | raise Exception | ||
337 | for line in child.stdout: | ||
338 | line = line.lstrip().split(' ', 1) | ||
339 | # perform a sanity check before calling the more expensive | ||
340 | # function to possibly extract the guest name | ||
341 | if ' -name ' in line[1] and gname == get_gname_from_pid(line[0]): | ||
342 | pids.append(int(line[0])) | ||
343 | child.stdout.close() | ||
344 | |||
345 | return pids | ||
346 | |||
347 | |||
323 | def get_gname_from_pid(pid): | 348 | def get_gname_from_pid(pid): |
324 | """Returns the guest name for a QEMU process pid. | 349 | """Returns the guest name for a QEMU process pid. |
325 | 350 | ||
@@ -977,7 +1002,7 @@ class Tui(object): | |||
977 | except re.error: | 1002 | except re.error: |
978 | continue | 1003 | continue |
979 | 1004 | ||
980 | def show_vm_selection(self): | 1005 | def show_vm_selection_by_pid(self): |
981 | """Draws PID selection mask. | 1006 | """Draws PID selection mask. |
982 | 1007 | ||
983 | Asks for a pid until a valid pid or 0 has been entered. | 1008 | Asks for a pid until a valid pid or 0 has been entered. |
@@ -1016,6 +1041,50 @@ class Tui(object): | |||
1016 | msg = '"' + str(pid) + '": Not a valid pid' | 1041 | msg = '"' + str(pid) + '": Not a valid pid' |
1017 | continue | 1042 | continue |
1018 | 1043 | ||
1044 | def show_vm_selection_by_guest_name(self): | ||
1045 | """Draws guest selection mask. | ||
1046 | |||
1047 | Asks for a guest name until a valid guest name or '' is entered. | ||
1048 | |||
1049 | """ | ||
1050 | msg = '' | ||
1051 | while True: | ||
1052 | self.screen.erase() | ||
1053 | self.screen.addstr(0, 0, | ||
1054 | 'Show statistics for specific guest.', | ||
1055 | curses.A_BOLD) | ||
1056 | self.screen.addstr(1, 0, | ||
1057 | 'This might limit the shown data to the trace ' | ||
1058 | 'statistics.') | ||
1059 | self.screen.addstr(5, 0, msg) | ||
1060 | curses.echo() | ||
1061 | self.screen.addstr(3, 0, "Guest [ENTER or guest]: ") | ||
1062 | gname = self.screen.getstr() | ||
1063 | curses.noecho() | ||
1064 | |||
1065 | if not gname: | ||
1066 | self.refresh_header(0) | ||
1067 | self.update_pid(0) | ||
1068 | break | ||
1069 | else: | ||
1070 | pids = [] | ||
1071 | try: | ||
1072 | pids = get_pid_from_gname(gname) | ||
1073 | except: | ||
1074 | msg = '"' + gname + '": Internal error while searching, ' \ | ||
1075 | 'use pid filter instead' | ||
1076 | continue | ||
1077 | if len(pids) == 0: | ||
1078 | msg = '"' + gname + '": Not an active guest' | ||
1079 | continue | ||
1080 | if len(pids) > 1: | ||
1081 | msg = '"' + gname + '": Multiple matches found, use pid ' \ | ||
1082 | 'filter instead' | ||
1083 | continue | ||
1084 | self.refresh_header(pids[0]) | ||
1085 | self.update_pid(pids[0]) | ||
1086 | break | ||
1087 | |||
1019 | def show_stats(self): | 1088 | def show_stats(self): |
1020 | """Refreshes the screen and processes user input.""" | 1089 | """Refreshes the screen and processes user input.""" |
1021 | sleeptime = DELAY_INITIAL | 1090 | sleeptime = DELAY_INITIAL |
@@ -1035,8 +1104,11 @@ class Tui(object): | |||
1035 | if char == 'f': | 1104 | if char == 'f': |
1036 | self.show_filter_selection() | 1105 | self.show_filter_selection() |
1037 | sleeptime = DELAY_INITIAL | 1106 | sleeptime = DELAY_INITIAL |
1107 | if char == 'g': | ||
1108 | self.show_vm_selection_by_guest_name() | ||
1109 | sleeptime = DELAY_INITIAL | ||
1038 | if char == 'p': | 1110 | if char == 'p': |
1039 | self.show_vm_selection() | 1111 | self.show_vm_selection_by_pid() |
1040 | sleeptime = DELAY_INITIAL | 1112 | sleeptime = DELAY_INITIAL |
1041 | except KeyboardInterrupt: | 1113 | except KeyboardInterrupt: |
1042 | break | 1114 | break |
@@ -1106,6 +1178,7 @@ Requirements: | |||
1106 | 1178 | ||
1107 | Interactive Commands: | 1179 | Interactive Commands: |
1108 | f filter by regular expression | 1180 | f filter by regular expression |
1181 | g filter by guest name | ||
1109 | p filter by PID | 1182 | p filter by PID |
1110 | q quit | 1183 | q quit |
1111 | x toggle reporting of stats for individual child trace events | 1184 | x toggle reporting of stats for individual child trace events |
@@ -1119,6 +1192,22 @@ Press any other key to refresh statistics immediately. | |||
1119 | else: | 1192 | else: |
1120 | return "" | 1193 | return "" |
1121 | 1194 | ||
1195 | def cb_guest_to_pid(option, opt, val, parser): | ||
1196 | try: | ||
1197 | pids = get_pid_from_gname(val) | ||
1198 | except: | ||
1199 | raise optparse.OptionValueError('Error while searching for guest ' | ||
1200 | '"{}", use "-p" to specify a pid ' | ||
1201 | 'instead'.format(val)) | ||
1202 | if len(pids) == 0: | ||
1203 | raise optparse.OptionValueError('No guest by the name "{}" ' | ||
1204 | 'found'.format(val)) | ||
1205 | if len(pids) > 1: | ||
1206 | raise optparse.OptionValueError('Multiple processes found (pids: ' | ||
1207 | '{}) - use "-p" to specify a pid ' | ||
1208 | 'instead'.format(" ".join(pids))) | ||
1209 | parser.values.pid = pids[0] | ||
1210 | |||
1122 | optparser = optparse.OptionParser(description=description_text, | 1211 | optparser = optparse.OptionParser(description=description_text, |
1123 | formatter=PlainHelpFormatter()) | 1212 | formatter=PlainHelpFormatter()) |
1124 | optparser.add_option('-1', '--once', '--batch', | 1213 | optparser.add_option('-1', '--once', '--batch', |
@@ -1158,6 +1247,14 @@ Press any other key to refresh statistics immediately. | |||
1158 | dest='pid', | 1247 | dest='pid', |
1159 | help='restrict statistics to pid', | 1248 | help='restrict statistics to pid', |
1160 | ) | 1249 | ) |
1250 | optparser.add_option('-g', '--guest', | ||
1251 | action='callback', | ||
1252 | type='string', | ||
1253 | dest='pid', | ||
1254 | metavar='GUEST', | ||
1255 | help='restrict statistics to guest by name', | ||
1256 | callback=cb_guest_to_pid, | ||
1257 | ) | ||
1161 | (options, _) = optparser.parse_args(sys.argv) | 1258 | (options, _) = optparser.parse_args(sys.argv) |
1162 | return options | 1259 | return options |
1163 | 1260 | ||
diff --git a/tools/kvm/kvm_stat/kvm_stat.txt b/tools/kvm/kvm_stat/kvm_stat.txt index 077bcc7e20dc..35587c3c2610 100644 --- a/tools/kvm/kvm_stat/kvm_stat.txt +++ b/tools/kvm/kvm_stat/kvm_stat.txt | |||
@@ -31,6 +31,8 @@ INTERACTIVE COMMANDS | |||
31 | [horizontal] | 31 | [horizontal] |
32 | *f*:: filter by regular expression | 32 | *f*:: filter by regular expression |
33 | 33 | ||
34 | *g*:: filter by guest name | ||
35 | |||
34 | *p*:: filter by PID | 36 | *p*:: filter by PID |
35 | 37 | ||
36 | *q*:: quit | 38 | *q*:: quit |
@@ -62,6 +64,10 @@ OPTIONS | |||
62 | --pid=<pid>:: | 64 | --pid=<pid>:: |
63 | limit statistics to one virtual machine (pid) | 65 | limit statistics to one virtual machine (pid) |
64 | 66 | ||
67 | -g<guest>:: | ||
68 | --guest=<guest_name>:: | ||
69 | limit statistics to one virtual machine (guest name) | ||
70 | |||
65 | -f<fields>:: | 71 | -f<fields>:: |
66 | --fields=<fields>:: | 72 | --fields=<fields>:: |
67 | fields to display (regex) | 73 | fields to display (regex) |