| #! /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() |