blob: 9a3d3f32398b673993fc8faa41b873324e3fb47d [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>
#
# This application is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2.
#
# This application is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
""" tuna - Application Tuning Program"""
import argparse
import os
import sys
import errno
import re
import getopt
import fnmatch
import gettext
import locale
from functools import reduce
import ethtool
import tuna.tuna_sched as tuna_sched
import procfs
from tuna import tuna, sysfs
import logging
import time
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
class HelpMessageParser(argparse.ArgumentParser):
def error(self, message):
sys.stderr.write('error: %s\n' % message)
self.print_help()
sys.exit(2)
def gen_parser():
POS = {
"cpu_list": dict(metavar='CPU-LIST', type=tuna.cpustring_to_list, help="CPU-LIST affected by commands"),
"thread_list": dict(metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"),
"filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"),
"profilename": dict(type=str, help="Apply changes described in this file"),
"run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""),
"priority": dict(type=tuna.get_policy_and_rtprio, help="policy/priority help"),
}
MODS = {
"logging": dict(dest='loglevel', metavar='LOG-LEVEL', type=get_loglevel, help="Log application details to file for given LOG-LEVEL"),
"debug" : dict(action='store_true', dest='debug', help='Print DEBUG level logging details to console'),
"version": dict(action='version', version='0.18', help="show version"),
"threads": dict(dest='thread_list', default=[], metavar='THREAD-LIST', type=threadstring_to_list, help="THREAD-LIST affected by commands"),
"irqs": dict(dest='irq_list', default=[], metavar='IRQ-LIST', type=irqstring_to_list, help="IRQ-LIST affect by commands"),
"cpus": dict(dest='cpu_list', default=[], metavar='CPU-LIST', type=tuna.cpustring_to_list, help='CPU-LIST affected by commands'),
"sockets": dict(dest='cpu_list', default=[], metavar='CPU-SOCKET-LIST', type=socketstring_to_list, help="CPU-SOCKET-LIST affected by commands"),
"show_sockets": dict(action='store_true', help='Show network sockets in use by threads'),
"cgroups": dict(action='store_true', dest='cgroups', help='Display the processes with the type of cgroups they are in'),
"affect_children": dict(action='store_true', help="Operation will affect children threads"),
"nohz_full": dict(action='store_true', help="CPUs in nohz_full kernel command line will be affected by operations"),
"no_uthreads": dict(action='store_false', dest='uthreads', help="Operations will not affect user threads"),
"no_kthreads": dict(action='store_false', dest='kthreads', help="Operations will not affect kernel threads"),
"disable_perf": dict(action='store_true', help="Explicitly disable usage of perf in GUI for process view"),
"refresh": dict(default=2500, metavar='MSEC', type=int, help="Refresh the GUI every MSEC milliseconds"),
"priority": dict(default=(None, None), type=tuna.get_policy_and_rtprio, help="Set thread scheduler tunables: POLICY and RTPRIO"),
"background": dict(action='store_true', help="Run command as background task")
}
parser = HelpMessageParser(description="tuna - Application Tuning Program")
parser._positionals.title = "commands"
parser.add_argument('-v', '--version', **MODS['version'])
parser.add_argument('-L', '--logging', **MODS['logging'])
parser.add_argument('-D', '--debug', **MODS['debug'])
subparser = parser.add_subparsers(dest='command')
isolate = subparser.add_parser('isolate', description="Move all allowed threads and IRQs away from CPU-LIST",
help="Move all allowed threads and IRQs away from CPU-LIST")
include = subparser.add_parser('include', description="Allow all threads to run on CPU-LIST",
help="Allow all threads to run on CPU-LIST")
move = subparser.add_parser('move', description="Move selected entities to CPU-LIST",
help="Move selected entities to CPU-LIST")
spread = subparser.add_parser('spread', description="Move selected entities to CPU-LIST",
help="Spread selected entities over CPU-LIST")
priority = subparser.add_parser('priority', description="Set thread scheduler tunables: POLICY and RTPRIO",
help="Set thread scheduler tunables: POLICY and RTPRIO")
run = subparser.add_parser('run', description="Fork a new process and run the COMMAND",
help="Fork a new process and run the COMMAND")
save = subparser.add_parser('save', description="Save kthreads sched tunables to FILENAME",
help="Save kthreads sched tunables to FILENAME")
apply = subparser.add_parser('apply', description="Apply changes described in profile",
help="Apply changes described in profile")
show_threads = subparser.add_parser('show_threads', description='Show thread list', help='Show thread list')
show_irqs = subparser.add_parser('show_irqs', description='Show IRQ list', help='Show IRQ list')
show_configs = subparser.add_parser('show_configs', description='List preloaded profiles', help='List preloaded profiles')
what_is = subparser.add_parser('what_is', description='Provides help about selected entities', help='Provides help about selected entities')
gui = subparser.add_parser('gui', description="Start the GUI", help="Start the GUI")
isolate_group = isolate.add_mutually_exclusive_group(required=True)
isolate_group.add_argument('-c', '--cpus', **MODS['cpus'])
isolate_group.add_argument('-S', '--sockets', **MODS['sockets'])
isolate_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
include_group = include.add_mutually_exclusive_group(required=True)
include_group.add_argument('-c', '--cpus', **MODS['cpus'])
include_group.add_argument('-S', '--sockets', **MODS['sockets'])
include_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
move_group = move.add_mutually_exclusive_group(required=True)
move_group.add_argument('-c', '--cpus', **MODS['cpus'])
move_group.add_argument('-S', '--sockets', **MODS['sockets'])
move_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
move.add_argument('-t', '--threads', **MODS['threads'])
move.add_argument('-q', '--irqs', **MODS['irqs'])
spread_group = spread.add_mutually_exclusive_group(required=True)
spread_group.add_argument('-c', '--cpus', **MODS['cpus'])
spread_group.add_argument('-S', '--sockets', **MODS['sockets'])
spread_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
spread.add_argument('-t', '--threads', **MODS['threads'])
spread.add_argument('-q', '--irqs', **MODS['irqs'])
priority.add_argument('priority', **POS['priority'])
priority.add_argument('-t', '--threads', **MODS['threads'], required=True)
priority.add_argument('-C', '--affect_children', **MODS['affect_children'])
run.add_argument('run_command', **POS['run_command'])
run_group = run.add_mutually_exclusive_group(required=False)
run_group.add_argument('-c', '--cpus', **MODS['cpus'])
run_group.add_argument('-S', '--sockets', **MODS['sockets'])
run_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
run.add_argument('-p', '--priority', **MODS['priority'])
run.add_argument('-b', '--background', **MODS['background'])
save.add_argument('filename', **POS['filename'])
save_group = save.add_mutually_exclusive_group(required=False)
save_group.add_argument('-c', '--cpus', **MODS['cpus'])
save_group.add_argument('-S', '--sockets', **MODS['sockets'])
save_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
save.add_argument('-t', '--threads', **MODS['threads'])
apply.add_argument('profilename', **POS['profilename'])
show_threads_group1 = show_threads.add_mutually_exclusive_group(required=False)
show_threads_group1.add_argument('-c', '--cpus', **MODS['cpus'])
show_threads_group1.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
show_threads_group1.add_argument('-S', '--sockets', **MODS['sockets'])
show_threads_group2 = show_threads.add_mutually_exclusive_group(required=False)
show_threads_group2.add_argument('-t', '--threads', **MODS['threads'])
show_threads_group2.add_argument('-q', '--irqs', **MODS['irqs'])
show_threads.add_argument('-U', '--no_uthreads', **MODS['no_uthreads'])
show_threads.add_argument('-K', '--no_kthreads', **MODS['no_kthreads'])
show_threads.add_argument('-C', '--affect_children', **MODS['affect_children'])
if have_inet_diag:
show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets'])
show_threads.add_argument('-G', '--cgroups', **MODS['cgroups'])
show_irqs_group = show_irqs.add_mutually_exclusive_group(required=False)
show_irqs_group.add_argument('-c', '--cpus', **MODS['cpus'])
show_irqs_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
show_irqs_group.add_argument('-S', '--sockets', **MODS['sockets'])
show_irqs.add_argument('-q', '--irqs', **MODS['irqs'])
what_is.add_argument('thread_list', **POS['thread_list'])
gui.add_argument('-d', '--disable_perf', **MODS['disable_perf'])
gui.add_argument('-R', '--refresh', **MODS['refresh'])
gui_group = gui.add_mutually_exclusive_group(required=False)
gui_group.add_argument('-c', '--cpus', **MODS['cpus'])
gui_group.add_argument('-N', '--nohz_full', **MODS['nohz_full'])
gui_group.add_argument('-S', '--sockets', **MODS['sockets'])
gui.add_argument('-U', '--no_uthreads', **MODS['no_uthreads'])
gui.add_argument('-K', '--no_kthreads', **MODS['no_kthreads'])
return parser
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("tuna: " + _("thread %d doesn't exists!") % tid)
return
pinfo = ps[tid]
cmdline = procfs.process_cmdline(pinfo)
help, title = tuna.kthread_help_plain_text(tid, cmdline)
print("%s\n\n%s" % (title, _(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 = "/proc/%s/fd" % pid
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):
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()
if cmd[:4] == "IRQ-":
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)
else:
u = cmd[cmd.find('-') + 1:]
if u in get_nics():
users = ethtool.get_module(u)
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
print(" %-5d " % pid if affect_children else " %-5d" % pid, end=' ')
print("%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity,
ctxt_switch_info, cmd, users), end=' ')
print(" %9s" % cgout if cgroups else "")
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)
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):
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()
for pid in ps_list:
ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info,
sock_inodes, sock_inode_re, cgroups)
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):
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)
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 threadstring_to_list(threadstr):
global ps
thread_list = []
thread_strings = list(set(threadstr.split(',')))
print(thread_strings)
for s in thread_strings:
if s.isdigit():
thread_list.append(int(s))
else:
ps = procfs.pidstats()
try:
thread_list += ps.find_by_regex(re.compile(fnmatch.translate(s)))
except:
thread_list += ps.find_by_name(s)
return thread_list
def irqstring_to_list(irqstr):
irq_list = []
irq_strings = list(set(irqstr.split(',')))
for s in irq_strings:
if s.isdigit():
irq_list.append(int(s))
else:
# find_by_user_regex returns a list of strings corresponding to irq number
irq_list_str = procfs.interrupts().find_by_user_regex(re.compile(fnmatch.translate(s)))
irq_list += [int(i) for i in irq_list_str if i.isdigit()]
return irq_list
def socketstring_to_list(socketstr):
cpu_list = []
socket_strings = list(set(socketstr.split(',')))
cpu_info = sysfs.cpus()
for s in socket_strings:
if s not in cpu_info.sockets:
print("tuna: invalid socket %(socket)s sockets available: %(available)s" %
{"socket": s,"available": ",".join(list(cpu_info.sockets.keys()))})
sys.exit(2)
cpu_list += [int(cpu.name[3:]) for cpu in cpu_info.sockets[s]]
return cpu_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 nohz_full_to_cpu():
try:
return tuna.nohz_full_list()
except:
print("tuna: --nohz_full " +
_(" needs nohz_full=cpulist on the kernel command line"))
sys.exit(2)
def main():
global ps
i18n_init()
parser = gen_parser()
# Set all necessary defaults for gui subparser if no arguments provided
args = parser.parse_args() if len(sys.argv) > 1 else parser.parse_args(['gui'])
if args.debug:
my_logger = setup_logging("my_logger")
my_logger.addHandler(add_handler("DEBUG", tofile=False))
my_logger.info("Debug option set")
if args.loglevel:
if not args.debug:
my_logger = setup_logging("my_logger")
try:
my_logger.addHandler(add_handler(args.loglevel, tofile=True))
my_logger.info("Logging option set at log level {}".format(args.loglevel))
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)\n")
if 'irq_list' in vars(args):
ps = procfs.pidstats()
if tuna.has_threaded_irqs(ps):
for irq in args.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.
if 'thread_list' in vars(args):
args.thread_list += irq_threads
if 'nohz_full' in vars(args) and args.nohz_full:
args.cpu_list = nohz_full_to_cpu()
if args.command in ['apply', 'a']:
apply_config(args.profilename)
elif args.command in ['include', 'I']:
tuna.include_cpus(args.cpu_list, get_nr_cpus())
elif args.command in ['isolate', 'i']:
tuna.isolate_cpus(args.cpu_list, get_nr_cpus())
elif args.command in ['run', 'r']:
tuna.run_command(args.run_command, args.priority[0], args.priority[1], args.cpu_list, args.background)
elif args.command in ['priority', 'p']:
try:
tuna.threads_set_priority(args.thread_list, args.priority, args.affect_children)
except OSError as err:
print("tuna: %s" % err)
sys.exit(2)
elif args.command in ['show_configs']:
list_config()
elif args.command in ['show_threads']:
do_ps(args.thread_list, args.cpu_list, args.irq_list, args.uthreads,
args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups)
elif args.command in ['show_irqs']:
show_irqs(args.irq_list, args.cpu_list)
elif args.command in ['move', 'm', 'spread', 'x']:
spread = args.command in ['spread', 'x']
if not (args.thread_list or args.irq_list):
parser.error("tuna: %s " % (args.command) + _("requires a thread/irq list!\n"))
if args.thread_list:
tuna.move_threads_to_cpu(args.cpu_list, args.thread_list, spread=spread)
if args.irq_list:
tuna.move_irqs_to_cpu(args.cpu_list, args.irq_list, spread=spread)
elif args.command in ['s', 'save']:
save(args.cpu_list, args.thread_list, args.filename)
elif args.command in ['W', 'what_is']:
for tid in args.thread_list:
thread_help(tid)
elif args.command in ['g', '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.'))
parser.print_help()
return
except RuntimeError:
print("tuna: machine needs to be authorized via xhost or ssh -X?")
return
try:
app = tuna_gui.main_gui(args.kthreads, args.uthreads, args.cpu_list, args.refresh, args.disable_perf)
app.run()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()