forked from ~ljy/RK356X_SDK_RELEASE

hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/tools/kvm/kvm_stat/kvm_stat
....@@ -1,4 +1,5 @@
1
-#!/usr/bin/python
1
+#!/usr/bin/env python3
2
+# SPDX-License-Identifier: GPL-2.0-only
23 #
34 # top-like utility for displaying kvm statistics
45 #
....@@ -8,8 +9,6 @@
89 # Authors:
910 # Avi Kivity <avi@redhat.com>
1011 #
11
-# This work is licensed under the terms of the GNU GPL, version 2. See
12
-# the COPYING file in the top-level directory.
1312 """The kvm_stat module outputs statistics about running KVM VMs
1413
1514 Three different ways of output formatting are available:
....@@ -26,14 +25,17 @@
2625 import locale
2726 import os
2827 import time
29
-import optparse
28
+import argparse
3029 import ctypes
3130 import fcntl
3231 import resource
3332 import struct
3433 import re
3534 import subprocess
35
+import signal
3636 from collections import defaultdict, namedtuple
37
+from functools import reduce
38
+from datetime import datetime
3739
3840 VMX_EXIT_REASONS = {
3941 'EXCEPTION_NMI': 0,
....@@ -226,6 +228,8 @@
226228 'DISABLE': 0x00002401,
227229 'RESET': 0x00002403,
228230 }
231
+
232
+signal_received = False
229233
230234 ENCODING = locale.getpreferredencoding(False)
231235 TRACE_FILTER = re.compile(r'^[^\(]*$')
....@@ -738,7 +742,11 @@
738742 The fields are all available KVM debugfs files
739743
740744 """
741
- return self.walkdir(PATH_DEBUGFS_KVM)[2]
745
+ exempt_list = ['halt_poll_fail_ns', 'halt_poll_success_ns']
746
+ fields = [field for field in self.walkdir(PATH_DEBUGFS_KVM)[2]
747
+ if field not in exempt_list]
748
+
749
+ return fields
742750
743751 def update_fields(self, fields_filter):
744752 """Refresh fields, applying fields_filter"""
....@@ -874,7 +882,7 @@
874882
875883 if options.debugfs:
876884 providers.append(DebugfsProvider(options.pid, options.fields,
877
- options.dbgfs_include_past))
885
+ options.debugfs_include_past))
878886 if options.tracepoints or not providers:
879887 providers.append(TracepointProvider(options.pid, options.fields))
880888
....@@ -975,15 +983,17 @@
975983 MAX_GUEST_NAME_LEN = 48
976984 MAX_REGEX_LEN = 44
977985 SORT_DEFAULT = 0
986
+MIN_DELAY = 0.1
987
+MAX_DELAY = 25.5
978988
979989
980990 class Tui(object):
981991 """Instruments curses to draw a nice text ui."""
982
- def __init__(self, stats):
992
+ def __init__(self, stats, opts):
983993 self.stats = stats
984994 self.screen = None
985995 self._delay_initial = 0.25
986
- self._delay_regular = DELAY_DEFAULT
996
+ self._delay_regular = opts.set_delay
987997 self._sorting = SORT_DEFAULT
988998 self._display_guests = 0
989999
....@@ -1184,7 +1194,7 @@
11841194
11851195 if not self._is_running_guest(self.stats.pid_filter):
11861196 if self._gname:
1187
- try: # ...to identify the guest by name in case it's back
1197
+ try: # ...to identify the guest by name in case it's back
11881198 pids = self.get_pid_from_gname(self._gname)
11891199 if len(pids) == 1:
11901200 self._refresh_header(pids[0])
....@@ -1283,7 +1293,8 @@
12831293 ' p filter by guest name/PID',
12841294 ' q quit',
12851295 ' r reset stats',
1286
- ' s set update interval',
1296
+ ' s set delay between refreshs (value range: '
1297
+ '%s-%s secs)' % (MIN_DELAY, MAX_DELAY),
12871298 ' x toggle reporting of stats for individual child trace'
12881299 ' events',
12891300 'Any other key refreshes statistics immediately')
....@@ -1337,8 +1348,8 @@
13371348 msg = ''
13381349 while True:
13391350 self.screen.erase()
1340
- self.screen.addstr(0, 0, 'Set update interval (defaults to %.1fs).' %
1341
- DELAY_DEFAULT, curses.A_BOLD)
1351
+ self.screen.addstr(0, 0, 'Set update interval (defaults to %.1fs).'
1352
+ % DELAY_DEFAULT, curses.A_BOLD)
13421353 self.screen.addstr(4, 0, msg)
13431354 self.screen.addstr(2, 0, 'Change delay from %.1fs to ' %
13441355 self._delay_regular)
....@@ -1349,11 +1360,9 @@
13491360 try:
13501361 if len(val) > 0:
13511362 delay = float(val)
1352
- if delay < 0.1:
1353
- msg = '"' + str(val) + '": Value must be >=0.1'
1354
- continue
1355
- if delay > 25.5:
1356
- msg = '"' + str(val) + '": Value must be <=25.5'
1363
+ err = is_delay_valid(delay)
1364
+ if err is not None:
1365
+ msg = err
13571366 continue
13581367 else:
13591368 delay = DELAY_DEFAULT
....@@ -1489,31 +1498,105 @@
14891498 pass
14901499
14911500
1492
-def log(stats):
1501
+class StdFormat(object):
1502
+ def __init__(self, keys):
1503
+ self._banner = ''
1504
+ for key in keys:
1505
+ self._banner += key.split(' ')[0] + ' '
1506
+
1507
+ def get_banner(self):
1508
+ return self._banner
1509
+
1510
+ def get_statline(self, keys, s):
1511
+ res = ''
1512
+ for key in keys:
1513
+ res += ' %9d' % s[key].delta
1514
+ return res
1515
+
1516
+
1517
+class CSVFormat(object):
1518
+ def __init__(self, keys):
1519
+ self._banner = 'timestamp'
1520
+ self._banner += reduce(lambda res, key: "{},{!s}".format(res,
1521
+ key.split(' ')[0]), keys, '')
1522
+
1523
+ def get_banner(self):
1524
+ return self._banner
1525
+
1526
+ def get_statline(self, keys, s):
1527
+ return reduce(lambda res, key: "{},{!s}".format(res, s[key].delta),
1528
+ keys, '')
1529
+
1530
+
1531
+def log(stats, opts, frmt, keys):
14931532 """Prints statistics as reiterating key block, multiple value blocks."""
1494
- keys = sorted(stats.get().keys())
1495
-
1496
- def banner():
1497
- for key in keys:
1498
- print(key.split(' ')[0], end=' ')
1499
- print()
1500
-
1501
- def statline():
1502
- s = stats.get()
1503
- for key in keys:
1504
- print(' %9d' % s[key].delta, end=' ')
1505
- print()
1533
+ global signal_received
15061534 line = 0
15071535 banner_repeat = 20
1536
+ f = None
1537
+
1538
+ def do_banner(opts):
1539
+ nonlocal f
1540
+ if opts.log_to_file:
1541
+ if not f:
1542
+ try:
1543
+ f = open(opts.log_to_file, 'a')
1544
+ except (IOError, OSError):
1545
+ sys.exit("Error: Could not open file: %s" %
1546
+ opts.log_to_file)
1547
+ if isinstance(frmt, CSVFormat) and f.tell() != 0:
1548
+ return
1549
+ print(frmt.get_banner(), file=f or sys.stdout)
1550
+
1551
+ def do_statline(opts, values):
1552
+ statline = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + \
1553
+ frmt.get_statline(keys, values)
1554
+ print(statline, file=f or sys.stdout)
1555
+
1556
+ do_banner(opts)
1557
+ banner_printed = True
15081558 while True:
15091559 try:
1510
- time.sleep(1)
1511
- if line % banner_repeat == 0:
1512
- banner()
1513
- statline()
1514
- line += 1
1560
+ time.sleep(opts.set_delay)
1561
+ if signal_received:
1562
+ banner_printed = True
1563
+ line = 0
1564
+ f.close()
1565
+ do_banner(opts)
1566
+ signal_received = False
1567
+ if (line % banner_repeat == 0 and not banner_printed and
1568
+ not (opts.log_to_file and isinstance(frmt, CSVFormat))):
1569
+ do_banner(opts)
1570
+ banner_printed = True
1571
+ values = stats.get()
1572
+ if (not opts.skip_zero_records or
1573
+ any(values[k].delta != 0 for k in keys)):
1574
+ do_statline(opts, values)
1575
+ line += 1
1576
+ banner_printed = False
15151577 except KeyboardInterrupt:
15161578 break
1579
+
1580
+ if opts.log_to_file:
1581
+ f.close()
1582
+
1583
+
1584
+def handle_signal(sig, frame):
1585
+ global signal_received
1586
+
1587
+ signal_received = True
1588
+
1589
+ return
1590
+
1591
+
1592
+def is_delay_valid(delay):
1593
+ """Verify delay is in valid value range."""
1594
+ msg = None
1595
+ if delay < MIN_DELAY:
1596
+ msg = '"' + str(delay) + '": Delay must be >=%s' % MIN_DELAY
1597
+ if delay > MAX_DELAY:
1598
+ msg = '"' + str(delay) + '": Delay must be <=%s' % MAX_DELAY
1599
+ return msg
15171600
15181601
15191602 def get_options():
....@@ -1546,89 +1629,98 @@
15461629 p filter by PID
15471630 q quit
15481631 r reset stats
1549
- s set update interval
1632
+ s set update interval (value range: 0.1-25.5 secs)
15501633 x toggle reporting of stats for individual child trace events
15511634 Press any other key to refresh statistics immediately.
15521635 """ % (PATH_DEBUGFS_KVM, PATH_DEBUGFS_TRACING)
15531636
1554
- class PlainHelpFormatter(optparse.IndentedHelpFormatter):
1555
- def format_description(self, description):
1556
- if description:
1557
- return description + "\n"
1558
- else:
1559
- return ""
1637
+ class Guest_to_pid(argparse.Action):
1638
+ def __call__(self, parser, namespace, values, option_string=None):
1639
+ try:
1640
+ pids = Tui.get_pid_from_gname(values)
1641
+ except:
1642
+ sys.exit('Error while searching for guest "{}". Use "-p" to '
1643
+ 'specify a pid instead?'.format(values))
1644
+ if len(pids) == 0:
1645
+ sys.exit('Error: No guest by the name "{}" found'
1646
+ .format(values))
1647
+ if len(pids) > 1:
1648
+ sys.exit('Error: Multiple processes found (pids: {}). Use "-p"'
1649
+ ' to specify the desired pid'
1650
+ .format(" ".join(map(str, pids))))
1651
+ namespace.pid = pids[0]
15601652
1561
- def cb_guest_to_pid(option, opt, val, parser):
1562
- try:
1563
- pids = Tui.get_pid_from_gname(val)
1564
- except:
1565
- sys.exit('Error while searching for guest "{}". Use "-p" to '
1566
- 'specify a pid instead?'.format(val))
1567
- if len(pids) == 0:
1568
- sys.exit('Error: No guest by the name "{}" found'.format(val))
1569
- if len(pids) > 1:
1570
- sys.exit('Error: Multiple processes found (pids: {}). Use "-p" '
1571
- 'to specify the desired pid'.format(" ".join(pids)))
1572
- parser.values.pid = pids[0]
1573
-
1574
- optparser = optparse.OptionParser(description=description_text,
1575
- formatter=PlainHelpFormatter())
1576
- optparser.add_option('-1', '--once', '--batch',
1577
- action='store_true',
1578
- default=False,
1579
- dest='once',
1580
- help='run in batch mode for one second',
1581
- )
1582
- optparser.add_option('-i', '--debugfs-include-past',
1583
- action='store_true',
1584
- default=False,
1585
- dest='dbgfs_include_past',
1586
- help='include all available data on past events for '
1587
- 'debugfs',
1588
- )
1589
- optparser.add_option('-l', '--log',
1590
- action='store_true',
1591
- default=False,
1592
- dest='log',
1593
- help='run in logging mode (like vmstat)',
1594
- )
1595
- optparser.add_option('-t', '--tracepoints',
1596
- action='store_true',
1597
- default=False,
1598
- dest='tracepoints',
1599
- help='retrieve statistics from tracepoints',
1600
- )
1601
- optparser.add_option('-d', '--debugfs',
1602
- action='store_true',
1603
- default=False,
1604
- dest='debugfs',
1605
- help='retrieve statistics from debugfs',
1606
- )
1607
- optparser.add_option('-f', '--fields',
1608
- action='store',
1609
- default='',
1610
- dest='fields',
1611
- help='''fields to display (regex)
1612
- "-f help" for a list of available events''',
1613
- )
1614
- optparser.add_option('-p', '--pid',
1615
- action='store',
1616
- default=0,
1617
- type='int',
1618
- dest='pid',
1619
- help='restrict statistics to pid',
1620
- )
1621
- optparser.add_option('-g', '--guest',
1622
- action='callback',
1623
- type='string',
1624
- dest='pid',
1625
- metavar='GUEST',
1626
- help='restrict statistics to guest by name',
1627
- callback=cb_guest_to_pid,
1628
- )
1629
- options, unkn = optparser.parse_args(sys.argv)
1630
- if len(unkn) != 1:
1631
- sys.exit('Error: Extra argument(s): ' + ' '.join(unkn[1:]))
1653
+ argparser = argparse.ArgumentParser(description=description_text,
1654
+ formatter_class=argparse
1655
+ .RawTextHelpFormatter)
1656
+ argparser.add_argument('-1', '--once', '--batch',
1657
+ action='store_true',
1658
+ default=False,
1659
+ help='run in batch mode for one second',
1660
+ )
1661
+ argparser.add_argument('-c', '--csv',
1662
+ action='store_true',
1663
+ default=False,
1664
+ help='log in csv format - requires option -l/-L',
1665
+ )
1666
+ argparser.add_argument('-d', '--debugfs',
1667
+ action='store_true',
1668
+ default=False,
1669
+ help='retrieve statistics from debugfs',
1670
+ )
1671
+ argparser.add_argument('-f', '--fields',
1672
+ default='',
1673
+ help='''fields to display (regex)
1674
+"-f help" for a list of available events''',
1675
+ )
1676
+ argparser.add_argument('-g', '--guest',
1677
+ type=str,
1678
+ help='restrict statistics to guest by name',
1679
+ action=Guest_to_pid,
1680
+ )
1681
+ argparser.add_argument('-i', '--debugfs-include-past',
1682
+ action='store_true',
1683
+ default=False,
1684
+ help='include all available data on past events for'
1685
+ ' debugfs',
1686
+ )
1687
+ argparser.add_argument('-l', '--log',
1688
+ action='store_true',
1689
+ default=False,
1690
+ help='run in logging mode (like vmstat)',
1691
+ )
1692
+ argparser.add_argument('-L', '--log-to-file',
1693
+ type=str,
1694
+ metavar='FILE',
1695
+ help="like '--log', but logging to a file"
1696
+ )
1697
+ argparser.add_argument('-p', '--pid',
1698
+ type=int,
1699
+ default=0,
1700
+ help='restrict statistics to pid',
1701
+ )
1702
+ argparser.add_argument('-s', '--set-delay',
1703
+ type=float,
1704
+ default=DELAY_DEFAULT,
1705
+ metavar='DELAY',
1706
+ help='set delay between refreshs (value range: '
1707
+ '%s-%s secs)' % (MIN_DELAY, MAX_DELAY),
1708
+ )
1709
+ argparser.add_argument('-t', '--tracepoints',
1710
+ action='store_true',
1711
+ default=False,
1712
+ help='retrieve statistics from tracepoints',
1713
+ )
1714
+ argparser.add_argument('-z', '--skip-zero-records',
1715
+ action='store_true',
1716
+ default=False,
1717
+ help='omit records with all zeros in logging mode',
1718
+ )
1719
+ options = argparser.parse_args()
1720
+ if options.csv and not (options.log or options.log_to_file):
1721
+ sys.exit('Error: Option -c/--csv requires -l/--log')
1722
+ if options.skip_zero_records and not (options.log or options.log_to_file):
1723
+ sys.exit('Error: Option -z/--skip-zero-records requires -l/-L')
16321724 try:
16331725 # verify that we were passed a valid regex up front
16341726 re.compile(options.fields)
....@@ -1694,6 +1786,10 @@
16941786 sys.stderr.write('Did you use a (unsupported) tid instead of a pid?\n')
16951787 sys.exit('Specified pid does not exist.')
16961788
1789
+ err = is_delay_valid(options.set_delay)
1790
+ if err is not None:
1791
+ sys.exit('Error: ' + err)
1792
+
16971793 stats = Stats(options)
16981794
16991795 if options.fields == 'help':
....@@ -1704,13 +1800,21 @@
17041800 sys.stdout.write(' ' + '\n '.join(sorted(set(event_list))) + '\n')
17051801 sys.exit(0)
17061802
1707
- if options.log:
1708
- log(stats)
1803
+ if options.log or options.log_to_file:
1804
+ if options.log_to_file:
1805
+ signal.signal(signal.SIGHUP, handle_signal)
1806
+ keys = sorted(stats.get().keys())
1807
+ if options.csv:
1808
+ frmt = CSVFormat(keys)
1809
+ else:
1810
+ frmt = StdFormat(keys)
1811
+ log(stats, options, frmt, keys)
17091812 elif not options.once:
1710
- with Tui(stats) as tui:
1813
+ with Tui(stats, options) as tui:
17111814 tui.show_stats()
17121815 else:
17131816 batch(stats)
17141817
1818
+
17151819 if __name__ == "__main__":
17161820 main()