blob: 38e7017336a5fe92e552f2efaab876c69a9865a0 [file] [log] [blame]
#! /usr/bin/python3
# -*- python -*-
# -*- coding: utf-8 -*-
# tuna - Application Tuning GUI
# Copyright (C) 2008, 2009, 2010, 2011 Red Hat Inc.
# Arnaldo Carvalho de Melo <acme@redhat.com>
#
# SPDX-License-Identifier: GPL-2.0-only
""" tuna - Application Tuning GUI """
import os
import sys
import errno
import re
import getopt
import fnmatch
import gettext
import locale
from functools import reduce
import tuna.new_eth as ethtool
import tuna.tuna_sched as tuna_sched
import procfs
from tuna import tuna, sysfs
import logging
import time
import shutil
def get_loglevel(level):
if level.isdigit() and int(level) in range(0,5):
# logging built-in module levels:
# 0 - NOTSET
# 10 - DEBUG
# 20 - INFO,
# 30 - WARNING
# 40 - ERROR
return int(level) * 10
level_str = level.upper()
if level_str == "CRITICAL":
raise ValueError("CRITICAL level is not supported by tuna")
return level_str
def setup_logging(logger_name):
logger = logging.getLogger(logger_name)
logger.setLevel(logging.DEBUG)
logger.propagate = False
return logger
def add_handler(loglevel, tofile=False):
formatter = logging.Formatter('[%(levelname)s] %(message)s')
if tofile:
lognum = 1
date = time.strftime("%Y%m%d")
while os.path.exists("tuna-{}-{}".format(date, lognum)):
lognum += 1
path = "tuna-{}-{}/log/Log".format(date,lognum)
os.makedirs(os.path.dirname(path), exist_ok=True, mode=0o777)
handler = logging.FileHandler(path)
else:
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(formatter)
handler.setLevel(loglevel)
return handler
try:
import inet_diag
have_inet_diag = True
except:
have_inet_diag = False
# FIXME: ETOOMANYGLOBALS, we need a class!
nr_cpus = None
ps = None
irqs = None
version = "0.18"
def usage():
print(_('Usage: tuna [OPTIONS]'))
fmt = '\t%-40s %s'
print(fmt % ('-h, --help', _('Give this help list')))
print(fmt % ('-a, --config_file_apply=profilename',
_('Apply changes described in profile')))
print(fmt % ('-l, --config_file_list',
_('List preloaded profiles')))
print(fmt % ('-g, --gui', _('Start the GUI')))
print(fmt % ('-G, --cgroup',
_('Display the processes with the type of cgroups they are in')))
print(fmt % ('-z, --spaced',
"Display spaced view for cgroups"))
print(fmt % ('-c, --cpus=' + _('CPU-LIST'), _('%(cpulist)s affected by commands') %
{"cpulist": _('CPU-LIST')}))
print(fmt % ('-C, --affect_children',
_('Operation will affect children threads')))
print(fmt % ('-d, --disable_perf',
_('Explicitly disable usage of perf in GUI for process view')))
print(fmt % ('-D, --debug', _('Print DEBUG level logging details to console')))
print(fmt % ('-f, --filter',
_('Display filter the selected entities')))
print(fmt % ('-i, --isolate', _('Move all allowed threads and IRQs away from %(cpulist)s') %
{"cpulist": _('CPU-LIST')}))
print(fmt % ('-I, --include', _('Allow all allowed threads and IRQs to run on %(cpulist)s') %
{"cpulist": _('CPU-LIST')}))
print(fmt % ('-K, --no_kthreads',
_('Operations will not affect kernel threads')))
print(fmt % ('-L, --logging',
_('Log application details to log file for given LOG-LEVEL')))
print(fmt % ('-m, --move', _('Move selected entities to %(cpulist)s') %
{"cpulist": _('CPU-LIST')}))
print(fmt % ('-N, --nohz_full',
_('CPUs in nohz_full= kernel command line will be affected by operations')))
if have_inet_diag:
print(fmt % ('-n, --show_sockets',
_('Show network sockets in use by threads')))
print(fmt % ('-p, --priority=[' +
_('POLICY') + ':]' +
_('RTPRIO'), _('Set thread scheduler tunables: %(policy)s and %(rtprio)s') %
{"policy": _('POLICY'), "rtprio": _('RTPRIO')}))
print(fmt % ('-P, --show_threads', _('Show thread list')))
print(fmt % ('-Q, --show_irqs', _('Show IRQ list')))
print(fmt % ('-q, --irqs=' + _('IRQ-LIST'), _('%(irqlist)s affected by commands') %
{"irqlist": _('IRQ-LIST')}))
print(fmt % ('-r, --run=' + _('COMMAND'), _('fork a new process and run the %(command)s') %
{"command": _('COMMAND')}))
print(fmt % ('-R, --refresh=' + _('MSEC'), _('Refresh the GUI every MSEC milliseconds')))
print(fmt % ('-s, --save=' + _('FILENAME'), _('Save kthreads sched tunables to %(filename)s') %
{"filename": _('FILENAME')}))
print(fmt % ('-S, --sockets=' +
_('CPU-SOCKET-LIST'), _('%(cpusocketlist)s affected by commands') %
{"cpusocketlist": _('CPU-SOCKET-LIST')}))
print(fmt % ('-t, --threads=' +
_('THREAD-LIST'), _('%(threadlist)s affected by commands') %
{"threadlist": _('THREAD-LIST')}))
print(fmt % ('-U, --no_uthreads',
_('Operations will not affect user threads')))
print(fmt % ('-v, --version', _('Show version')))
print(fmt % ('-W, --what_is',
_('Provides help about selected entities')))
print(fmt % ('-x, --spread', _('Spread selected entities over %(cpulist)s') %
{"cpulist": _('CPU-LIST')}))
def get_nr_cpus():
""" Get all cpus including disabled cpus """
global nr_cpus
if nr_cpus:
return nr_cpus
nr_cpus = os.sysconf('SC_NPROCESSORS_CONF')
return nr_cpus
nics = None
def get_nics():
global nics
if nics:
return nics
nics = ethtool.get_active_devices()
return nics
def thread_help(tid):
global ps
if not ps:
ps = procfs.pidstats()
if tid not in ps:
print(f"tuna: thread {tid} doesn't exist!")
return
pinfo = ps[tid]
cmdline = procfs.process_cmdline(pinfo)
help, title = tuna.kthread_help_plain_text(tid, cmdline)
print(title, "\n\n")
if help.isspace():
help = "No help description available."
print(help)
def save(cpu_list, thread_list, filename):
kthreads = tuna.get_kthread_sched_tunings()
for name in list(kthreads.keys()):
kt = kthreads[name]
if (cpu_list and not set(kt.affinity).intersection(set(cpu_list))) or \
(thread_list and kt.pid not in thread_list):
del kthreads[name]
tuna.generate_rtgroups(filename, kthreads, get_nr_cpus())
def ps_show_header(has_ctxt_switch_info, cgroups=False):
print("%7s %6s %5s %7s %s" %
(" ", " ", " ", _("thread"),
has_ctxt_switch_info and "ctxt_switches" or ""))
print("%7s %6s %5s %7s%s %15s" % ("pid", "SCHED_", "rtpri", "affinity",
has_ctxt_switch_info and " %9s %12s" % (
"voluntary", "nonvoluntary")
or "", "cmd"), end=' ')
print(" %7s" % ("cgroup") if cgroups else "")
def ps_show_sockets(pid, ps, inodes, inode_re, indent=0):
header_printed = False
dirname = f"/proc/{pid}/fd"
try:
filenames = os.listdir(dirname)
except: # Process died
return
sindent = " " * indent
for filename in filenames:
pathname = os.path.join(dirname, filename)
try:
linkto = os.readlink(pathname)
except: # Process died
continue
inode_match = inode_re.match(linkto)
if not inode_match:
continue
inode = int(inode_match.group(1))
if inode not in inodes:
continue
if not header_printed:
print("%s%-10s %-6s %-6s %15s:%-5s %15s:%-5s" %
(sindent, "State", "Recv-Q", "Send-Q",
"Local Address", "Port",
"Peer Address", "Port"))
header_printed = True
s = inodes[inode]
print("%s%-10s %-6d %-6d %15s:%-5d %15s:%-5d" %
(sindent, s.state(),
s.receive_queue(), s.write_queue(),
s.saddr(), s.sport(), s.daddr(), s.dport()))
def format_affinity(affinity):
if len(affinity) <= 4:
return ",".join(str(a) for a in affinity)
return ",".join(str(hex(a)) for a in procfs.hexbitmask(affinity, get_nr_cpus()))
def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
sock_inode_re, cgroups, columns=None, compact=True):
global irqs
try:
affinity = format_affinity(os.sched_getaffinity(pid))
except OSError as e:
if e.args[0] == errno.ESRCH:
return
raise e
sched = tuna_sched.sched_str(os.sched_getscheduler(pid))[6:]
rtprio = int(ps[pid]["stat"]["rt_priority"])
cgout = ps[pid]["cgroups"]
cmd = ps[pid]["stat"]["comm"]
users = ""
if tuna.is_irq_thread(cmd):
try:
if not irqs:
irqs = procfs.interrupts()
users = irqs[tuna.irq_thread_number(cmd)]["users"]
for u in users:
if u in get_nics():
users[users.index(u)] = "%s(%s)" % (
u, ethtool.get_module(u))
users = ",".join(users)
except:
users = "Not found in /proc/interrupts!"
ctxt_switch_info = ""
if has_ctxt_switch_info:
voluntary_ctxt_switches = int(
ps[pid]["status"]["voluntary_ctxt_switches"])
nonvoluntary_ctxt_switches = int(
ps[pid]["status"]["nonvoluntary_ctxt_switches"])
ctxt_switch_info = " %9d %12s" % (voluntary_ctxt_switches,
nonvoluntary_ctxt_switches)
# Indent affected children
s1 = " %-5d " % pid if affect_children else " %-5d" % pid
print(s1, end=' ')
s2 = "%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity,
ctxt_switch_info, cmd, users)
print(s2, end=' ')
if cgroups:
length = int(columns) - len(s1 + " ") - len(s2 + " ")
if len(" %9s" % cgout) <= length:
print("%s" % cgout)
else:
print("\n %s" % cgout + ("" if compact else "\n"))
else:
print()
if sock_inodes:
ps_show_sockets(pid, ps, sock_inodes, sock_inode_re,
affect_children and 3 or 4)
if affect_children and "threads" in ps[pid]:
for tid in list(ps[pid]["threads"].keys()):
ps_show_thread(tid, False, ps[pid]["threads"],
has_ctxt_switch_info,
sock_inodes, sock_inode_re, cgroups, columns, compact)
def ps_show(ps, affect_children, thread_list, cpu_list,
irq_list_numbers, show_uthreads, show_kthreads,
has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact):
ps_list = []
for pid in list(ps.keys()):
iskth = tuna.iskthread(pid)
if not show_uthreads and not iskth:
continue
if not show_kthreads and iskth:
continue
in_irq_list = False
if irq_list_numbers:
if tuna.is_hardirq_handler(ps, pid):
try:
irq = int(ps[pid]["stat"]["comm"][4:])
if irq not in irq_list_numbers:
if not thread_list:
continue
else:
in_irq_list = True
except:
pass
elif not thread_list:
continue
if not in_irq_list and thread_list and pid not in thread_list:
continue
try:
affinity = os.sched_getaffinity(pid)
except OSError as e:
if e.args[0] == errno.ESRCH:
continue
raise e
if cpu_list and not set(cpu_list).intersection(set(affinity)):
continue
ps_list.append(pid)
ps_list.sort()
# Width of terminal in columns
columns = 80
if cgroups:
if os.isatty(sys.stdout.fileno()):
columns = shutil.get_terminal_size().columns
for pid in ps_list:
ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info,
sock_inodes, sock_inode_re, cgroups, columns, compact)
def load_socktype(socktype, inodes):
idiag = inet_diag.create(socktype=socktype)
while True:
try:
s = idiag.get()
except:
break
inodes[s.inode()] = s
def load_sockets():
inodes = {}
for socktype in (inet_diag.TCPDIAG_GETSOCK, inet_diag.DCCPDIAG_GETSOCK):
load_socktype(socktype, inodes)
return inodes
def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads,
affect_children, show_sockets, cgroups, compact):
ps = procfs.pidstats()
if affect_children:
ps.reload_threads()
sock_inodes = None
sock_inode_re = None
if show_sockets:
sock_inodes = load_sockets()
sock_inode_re = re.compile(r"socket:\[(\d+)\]")
has_ctxt_switch_info = "voluntary_ctxt_switches" in ps[1]["status"]
try:
if sys.stdout.isatty():
ps_show_header(has_ctxt_switch_info, cgroups)
ps_show(ps, affect_children, thread_list,
cpu_list, irq_list, show_uthreads, show_kthreads,
has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact)
except IOError:
# 'tuna -P | head' for instance
pass
def find_drivers_by_users(users):
nics = get_nics()
drivers = []
for u in users:
try:
idx = u.index('-')
u = u[:idx]
except:
pass
if u in nics:
driver = ethtool.get_module(u)
if driver not in drivers:
drivers.append(driver)
return drivers
def show_irqs(irq_list, cpu_list):
global irqs
if not irqs:
irqs = procfs.interrupts()
if sys.stdout.isatty():
print("%4s %-16s %8s" % ("#", _("users"), _("affinity"),))
sorted_irqs = []
for k in list(irqs.keys()):
try:
irqn = int(k)
affinity = irqs[irqn]["affinity"]
except:
continue
if irq_list and irqn not in irq_list:
continue
if cpu_list and not set(cpu_list).intersection(set(affinity)):
continue
sorted_irqs.append(irqn)
sorted_irqs.sort()
for irq in sorted_irqs:
affinity = format_affinity(irqs[irq]["affinity"])
users = irqs[irq]["users"]
print("%4d %-16s %8s" % (irq, ",".join(users), affinity), end=' ')
drivers = find_drivers_by_users(users)
if drivers:
print(" %s" % ",".join(drivers))
else:
print()
def do_list_op(op, current_list, op_list):
if not current_list:
current_list = []
if op == '+':
return list(set(current_list + op_list))
if op == '-':
return list(set(current_list) - set(op_list))
return list(set(op_list))
def thread_mapper(s):
global ps
try:
return [int(s), ]
except:
pass
ps = procfs.pidstats()
try:
return ps.find_by_regex(re.compile(fnmatch.translate(s)))
except:
return ps.find_by_name(s)
def irq_mapper(s):
global irqs
try:
return [int(s), ]
except:
pass
if not irqs:
irqs = procfs.interrupts()
irq_list_str = irqs.find_by_user_regex(re.compile(fnmatch.translate(s)))
irq_list = []
for i in irq_list_str:
try:
irq_list.append(int(i))
except:
pass
return irq_list
def pick_op(argument):
if argument == "":
return (None, argument)
if argument[0] in ('+', '-'):
return (argument[0], argument[1:])
return (None, argument)
def i18n_init():
(app, localedir) = ('tuna', '/usr/share/locale')
locale.setlocale(locale.LC_ALL, '')
gettext.bindtextdomain(app, localedir)
gettext.textdomain(app)
gettext.install(app, localedir)
def apply_config(filename):
from tuna.config import Config
config = Config()
if os.path.exists(filename):
config.config['root'] = os.getcwd() + "/"
filename = os.path.basename(filename)
else:
if not os.path.exists(config.config['root']+filename):
print(filename + _(" not found!"))
sys.exit(1)
if config.loadTuna(filename):
sys.exit(1)
ctrl = 0
values = {}
values['toapply'] = {}
for index in range(len(config.ctlParams)):
for opt in config.ctlParams[index]:
values['toapply'][ctrl] = {}
values['toapply'][ctrl]['label'] = opt
values['toapply'][ctrl]['value'] = config.ctlParams[index][opt]
ctrl = ctrl + 1
config.applyChanges(values)
def list_config():
from tuna.config import Config
config = Config()
print(_("Preloaded config files:"))
for value in config.populate():
print(value)
sys.exit(1)
def main():
global ps
i18n_init()
try:
short = "a:c:dDCfgGzhiIKlmNp:PQq:r:R:s:S:t:UvWxL:"
long = ["cpus=", "affect_children", "filter", "gui", "help",
"isolate", "include", "no_kthreads", "move", "nohz_full",
"show_sockets", "priority=", "show_threads",
"show_irqs", "irqs=",
"save=", "sockets=", "threads=", "no_uthreads",
"version", "what_is", "spread", "cgroup", "spaced", "config_file_apply=",
"config_file_list", "run=", "refresh=", "disable_perf", "logging=", "debug"]
if have_inet_diag:
short += "n"
long.append("show_sockets")
opts, args = getopt.getopt(sys.argv[1:], short, long)
except getopt.GetoptError as err:
usage()
print(str(err))
sys.exit(2)
run_gui = not opts
kthreads = True
uthreads = True
cgroups = False
compact = True
cpu_list = None
debug = False
irq_list = None
irq_list_str = None
log = False
rtprio = None
policy = None
thread_list = []
thread_list_str = None
filter = False
affect_children = False
show_sockets = False
p_waiting_action = False
gui_refresh = 2500
disable_perf = False
for o, a in opts:
if o in ("-h", "--help"):
usage()
return
if o in ("-D", "--debug"):
if debug:
my_logger.warning("Debugging already set")
continue
debug = True
if not log:
my_logger = setup_logging("my_logger")
my_logger.addHandler(add_handler("DEBUG", tofile=False))
my_logger.info("Debug option set")
if o in ("-L", "--logging"):
if log:
my_logger.warning("Logging already set")
continue
log = True
loglevel = get_loglevel(a)
if not debug:
my_logger = setup_logging("my_logger")
try:
my_logger.addHandler(add_handler(loglevel, tofile=True))
except ValueError as e:
print(e, "tuna: --logging requires valid logging level\n")
print("Valid log levels: NOTSET, DEBUG, INFO, WARNING, ERROR")
print("Log levels may be specified numerically (0-4)")
my_logger.info("Logging option set")
if o in ("-a", "--config_file_apply"):
apply_config(a)
elif o in ("-l", "--config_file_list"):
list_config()
elif o in ("-c", "--cpus"):
(op, a) = pick_op(a)
try:
op_list = tuna.cpustring_to_list(a)
except ValueError:
usage()
return
cpu_list = do_list_op(op, cpu_list, op_list)
elif o in ("-N", "--nohz_full"):
try:
cpu_list = tuna.nohz_full_list()
except:
print("tuna: --nohz_full " +
_(" needs nohz_full=cpulist on the kernel command line"))
sys.exit(2)
elif o in ("-C", "--affect_children"):
affect_children = True
elif o in ("-G", "--cgroup"):
cgroups = True
elif o in ("-z", "--spaced"):
compact = False
elif o in ("-t", "--threads"):
# The -t - will reset thread list
if a == '-':
thread_list = []
thread_list_str = ''
else:
(op, a) = pick_op(a)
op_list = reduce(lambda i, j: i + j,
list(map(thread_mapper, a.split(","))))
op_list = list(set(op_list))
thread_list = do_list_op(op, thread_list, op_list)
# Check if a process name was specified and no
# threads was found, which would result in an empty
# thread list, i.e. we would print all the threads
# in the system when we should print nothing.
if not op_list and isinstance(a, type('')):
thread_list_str = do_list_op(op, thread_list_str,
a.split(","))
if not op:
irq_list = None
elif o in ("-f", "--filter"):
filter = True
elif o in ("-g", "--gui"):
run_gui = True
elif o in ("-R", "--refresh"):
run_gui = True
(op, a) = pick_op(a)
try:
gui_refresh=int(a)
except Exception as err:
print(f"tuna: --refresh {err}")
sys.exit(2)
elif o in ("-d", "--disable_perf"):
run_gui = True
disable_perf = True
elif o in ("-i", "--isolate"):
if not cpu_list:
print("tuna: --isolate " + _("requires a cpu list!"))
sys.exit(2)
tuna.isolate_cpus(cpu_list, get_nr_cpus())
elif o in ("-I", "--include"):
if not cpu_list:
print("tuna: --include " + _("requires a cpu list!"))
sys.exit(2)
tuna.include_cpus(cpu_list, get_nr_cpus())
elif o in ("-p", "--priority"):
# Save policy and rtprio for future Actions (e.g. --run).
try:
(policy, rtprio) = tuna.get_policy_and_rtprio(a)
except ValueError:
print("tuna: " + _("\"%s\" is an unsupported priority value!") % a)
sys.exit(2)
if not thread_list:
# For backward compatibility
p_waiting_action = True
else:
try:
tuna.threads_set_priority(thread_list, a, affect_children)
except OSError as err:
print(f"tuna: {err}")
sys.exit(2)
elif o in ("-P", "--show_threads"):
# If the user specified process names that weren't
# resolved to pids, don't show all threads.
if not thread_list and not irq_list:
if thread_list_str or irq_list_str:
continue
do_ps(thread_list, cpu_list, irq_list, uthreads,
kthreads, affect_children, show_sockets, cgroups, compact)
elif o in ("-Q", "--show_irqs"):
# If the user specified IRQ names that weren't
# resolved to IRQs, don't show all IRQs.
if not irq_list and irq_list_str:
continue
show_irqs(irq_list, cpu_list)
elif o in ("-n", "--show_sockets"):
show_sockets = True
elif o in ("-m", "--move", "-x", "--spread"):
spread = o in ("-x", "--spread")
if not cpu_list:
print("tuna: %s " % ("--spread" if spread else "--move") + _("requires a cpu list!"))
sys.exit(2)
if not (thread_list or irq_list):
print("tuna: %s " % ("--spread" if spread else "--move") + _("requires a list of threads/irqs!"))
sys.exit(2)
if thread_list:
tuna.move_threads_to_cpu(cpu_list, thread_list, spread=spread)
if irq_list:
tuna.move_irqs_to_cpu(cpu_list, irq_list, spread=spread)
elif o in ("-s", "--save"):
save(cpu_list, thread_list, a)
elif o in ("-S", "--sockets"):
(op, a) = pick_op(a)
sockets = list(a.split(','))
if not cpu_list:
cpu_list = []
cpu_info = sysfs.cpus()
op_list = []
for socket in sockets:
if socket not in cpu_info.sockets:
print("tuna: %s" %
(_("invalid socket %(socket)s sockets available: %(available)s") %
{"socket": socket,
"available": ",".join(list(cpu_info.sockets.keys()))}))
sys.exit(2)
op_list += [int(cpu.name[3:])
for cpu in cpu_info.sockets[socket]]
cpu_list = do_list_op(op, cpu_list, op_list)
elif o in ("-K", "--no_kthreads"):
kthreads = False
elif o in ("-q", "--irqs"):
(op, a) = pick_op(a)
op_list = reduce(lambda i, j: i + j,
list(map(irq_mapper, list(set(a.split(","))))))
irq_list = do_list_op(op, irq_list, op_list)
# See comment above about thread_list_str
if not op_list and isinstance(a, type('')):
irq_list_str = do_list_op(op, irq_list_str, a.split(","))
if not op:
thread_list = []
if not ps:
ps = procfs.pidstats()
if tuna.has_threaded_irqs(ps):
for irq in irq_list:
irq_re = tuna.threaded_irq_re(irq)
irq_threads = ps.find_by_regex(irq_re)
if irq_threads:
# Change the affinity of the thread too
# as we can't rely on changing the irq
# affinity changing the affinity of the
# thread or vice versa. We need to change
# both.
thread_list += irq_threads
elif o in ("-U", "--no_uthreads"):
uthreads = False
elif o in ("-v", "--version"):
print(version)
elif o in ("-W", "--what_is"):
if not thread_list:
print("tuna: --what_is " + _("requires a thread list!"))
sys.exit(2)
for tid in thread_list:
thread_help(tid)
elif o in ("-r", "--run"):
# If -p is set, it will be consumed. So, no backward compatible
# error handling action must be taken.
p_waiting_action = False
# pick_op() before run the command: to remove the prefix
# + or - from command line.
(op, a) = pick_op(a)
# In order to include the new process, it must run
# the command first, and then get the list of pids,
tuna.run_command(a, policy, rtprio, cpu_list)
op_list = reduce(lambda i, j: i + j,
list(map(thread_mapper, a.split(","))))
op_list = list(set(op_list))
thread_list = do_list_op(op, thread_list, op_list)
# Check if a process name was specified and no
# threads was found, which would result in an empty
# thread list, i.e. we would print all the threads
# in the system when we should print nothing.
if not op_list and isinstance(a, type('')):
thread_list_str = do_list_op(op, thread_list_str, a.split(","))
if not op:
irq_list = None
# For backward compatibility: when -p used to be only an Action, it
# used to exit(2) if no action was taken (i.e. if no threads_list
# was set).
if p_waiting_action:
print(("tuna: -p ") + _("requires a thread list!"))
sys.exit(2)
if run_gui:
try:
from tuna import tuna_gui
except ImportError:
# gui packages not installed
print(_('tuna: packages needed for the GUI missing.'))
print(_(' Make sure xauth, pygtk2-libglade are installed.'))
usage()
return
except RuntimeError:
print("tuna: machine needs to be authorized via xhost or ssh -X?")
return
try:
cpus_filtered = filter if cpu_list else []
app = tuna_gui.main_gui(kthreads, uthreads, cpus_filtered, gui_refresh, disable_perf)
app.run()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()