Changeset 2751

Show
Ignore:
Timestamp:
02/03/18 00:30:56 (10 months ago)
Author:
jwoithe
Message:

Simplify and optimize ffado-diag.in.

Executing ffado-diag after preprocessing ensures that the shebang python
version is used without keeping SConscript in sync with ffado-diag.in.

Merging all *.py modules into a single script will help any contributor
figuring the big picture, and spares the sys.path trick.

The rewrite is more resilient to missing dependencies, and hopefully more
readable.

Patch contributed by Nicolas Boulenguez with minor adjustments by Jonathan
Woithe. It has been tested under python3 and python2.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/libffado/support/tools/ffado-diag.1

    r2089 r2751  
    1 .TH FFADO-DIAG 1 27-Mar-2012 "ffado-diag" 
     1.TH FFADO\-DIAG 1 27\-Mar\-2012 "ffado\-diag" 
    22.SH NAME 
    3 ffado-diag \- print system diagnostic information related to FFADO. 
     3ffado\-diag \- print system diagnostic information related to FFADO. 
     4\" 
    45.SH SYNOPSIS 
    5 .BI "ffado-diag" 
    6 .sp 
     6.BR ffado\-diag [\| \-\-static \||\| \-V \||\| \-\-version \||\| \-\-usage \|] 
     7\" 
    78.SH DESCRIPTION 
    8 .B ffado-diag 
     9.B ffado\-diag 
    910prints out an extensive collection of diagnostic information about the 
    1011computer it is run on.  Information included is the FFADO version number, 
     
    1213interface card, interrupt usage, and so on.  This is useful for developers 
    1314to know when giving support via the FFADO mailing lists.  For anything other 
    14 than trivial issues, the output of  
    15 .B 
    16 ffado-diag 
     15than trivial issues, the output of 
     16.B ffado\-diag 
    1717will be one of the first things asked fro when debugging a problem. 
    18 .SH "OPTIONS" 
    19 .B 
    20 ffado-diag 
    21 does not presently have any command line options. 
     18\" 
     19.SH OPTIONS 
     20.BR \-V ", " \-\-version 
     21Display version information. 
     22.B \-\-usage 
     23Print a short usage message and exit. 
     24.B \-\-static 
     25Only display executable paths and libraries. 
  • trunk/libffado/support/tools/ffado-diag.in

    r2750 r2751  
    11#!$PYTHON_INTERPRETER 
    2 
    3  
    4 
    5 # Copyright (C) 2008 Pieter Palmers 
    6 #               2009-2010 Arnold Krille 
    7 
     2# Dollar variables are preprocessed by SConscript at build time. 
     3version_info = "FFADO diagnostic utility $VERSION$REVISIONSTRING" 
     4copyright_info = """ 
     5(C) 2008 Pieter Palmers 
     6    2009-2010 Arnold Krille 
     7    2018 Nicolas Boulenguez, Jonathan Woithe 
     8""" 
     9static_info = "$PYTHONDIR/static_info.txt" 
    810# This program is free software: you can redistribute it and/or modify 
    911# it under the terms of the GNU General Public License as published by 
    1012# the Free Software Foundation, version 3 of the License. 
    11 
     13 
    1214# This program is distributed in the hope that it will be useful, 
    1315# but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1416# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1517# GNU General Public License for more details. 
    16 
     18 
    1719# You should have received a copy of the GNU General Public License 
    1820# along with this program.  If not, see <http://www.gnu.org/licenses/>. 
    19 
    20  
    21 
     21 
    2222# Test for common FFADO problems 
    23 
    24  
    25 # Make "print(..., end='')" functionality available under Python2 
    26 from __future__ import print_function 
    27  
     23 
     24import glob 
     25import os.path 
     26import re 
     27import subprocess 
    2828import sys 
    2929 
    30 # Add the path of the installed dependent files 
    31 sys.path.insert(0, "$PYTHONDIR" ) 
    32  
    33 import listirqinfo 
    34 import helpstrings 
    35 from ffado_diag_helpers import * 
    36  
    37 ## message strings 
    38 FFADODIAG_VERSION = "$VERSION$REVISIONSTRING" 
    39  
    40 welcome_msg = """ 
    41  
    42 FFADO diagnostic utility """ + FFADODIAG_VERSION + """ 
    43 ============================ 
    44 (C) 2008 Pieter Palmers 
    45     2009-2010 Arnold Krille 
    46  
    47 """ 
    48  
    49 ## main program 
    50 if __name__== '__main__': 
    51  
    52     print (welcome_msg) 
    53  
    54     parse_command_line () 
    55  
    56     print ("=== CHECK ===") 
    57     print (" Base system...") 
    58  
    59     # check kernel 
    60     kernel_version = get_kernel_version() 
    61     print ("  kernel version............ " + str(kernel_version)) 
    62     kernel_is_preempt = get_kernel_preempt() 
    63     print ("    Preempt (low latency)... " + str(kernel_is_preempt)) 
    64     # Hint: The main parts of the rt patches are in mainline-kernels nowadays. Performance with stock kernels is sufficient... 
    65     kernel_is_rt_patched = get_kernel_rt_patched() 
    66     print ("    RT patched.............. " + str(kernel_is_rt_patched)) 
    67  
    68     # check modules 
    69     oldstack_present = check_1394oldstack_present() 
    70     oldstack_loaded = check_1394oldstack_loaded() 
    71     oldstack_active = check_1394oldstack_active() 
    72     oldstack_statically_linked = not check_1394oldstack_loaded() and check_1394oldstack_linked() 
    73     newstack_present = check_1394newstack_present() 
    74     newstack_loaded = check_1394newstack_loaded() 
    75     newstack_active = check_1394newstack_active() 
    76     newstack_statically_linked = not check_1394newstack_loaded() and check_1394newstack_linked() 
    77  
    78     print ("  old 1394 stack present.... " + str(oldstack_present)) 
    79     print ("  old 1394 stack loaded..... " + str(oldstack_loaded)) 
    80     print ("  old 1394 stack active..... " + str(oldstack_active)) 
    81     print ("  new 1394 stack present.... " + str(newstack_present)) 
    82     print ("  new 1394 stack loaded..... " + str(newstack_loaded)) 
    83     print ("  new 1394 stack active..... " + str(newstack_active)) 
    84  
    85     # check /dev/raw1394 node presence 
    86     devnode_present = check_1394oldstack_devnode_present() 
    87     print ("  /dev/raw1394 node present. " + str(devnode_present)) 
    88     if devnode_present: 
    89         # check /dev/raw1394 access permissions 
    90         devnode_permissions = check_1394oldstack_devnode_permissions() 
    91         print ("  /dev/raw1394 permissions.. " + str(devnode_permissions)) 
     30# Prefer reproducible output with few non-ASCII characters. 
     31os.environ ["LC_ALL"] = "C" 
     32 
     33# Consistent formatting. 
     34def show_pair (key, value): 
     35    # rstrip () for convenience, but do not assume that value is a string. 
     36    print ("{:25} {}".format (key, value).rstrip ()) 
     37def indent (lines): 
     38    print ("  {}".format (lines.rstrip ().replace ("\n", "\n  "))) 
     39 
     40# Convenient shortcuts. 
     41def stdout (*args): 
     42    return subprocess.check_output (args).decode ("utf8") 
     43 
     44def which (command): 
     45    popen = subprocess.Popen (("which", command), stdout=subprocess.PIPE, 
     46                              stderr=subprocess.PIPE) 
     47    stdout, stderr = popen.communicate () 
     48    if popen.returncode == 0: 
     49        return stdout.decode ("utf8").rstrip () 
     50    elif popen.returncode == 1: 
     51        return None 
    9252    else: 
    93         devnode_permissions = None 
    94  
    95     if newstack_active: 
    96         # check permissions 
    97         newstack_permissions = get_juju_permissions() 
    98         print ("  /dev/fw* permissions:") 
    99         print (newstack_permissions) 
    100  
    101     print ("  User IDs:") 
    102     print (get_user_ids()) 
    103  
    104  
    105     # check libraries 
    106     print(" Prerequisites (dynamic at run-time)...") 
    107     check_libraries () 
    108  
    109     print (" Prerequisites (static at compile-time)...") 
    110     try: 
    111         with open( "$PYTHONDIR/static_info.txt", "r" ) as f: 
    112             for line in f: 
    113                 if line.startswith("  "): 
    114                     print (line, end='') 
    115     except: 
    116         print ("Failed to read $PYTHONDIR/static_info.txt.") 
    117  
    118     # libraw 
    119  
    120     print (" uname -a...") 
    121     print ("   " + run_command_string (('uname', '-a'))) 
    122  
    123     print (" Hardware...") 
    124     # check host controller 
    125     print ("   Host controllers:") 
    126     list_host_controllers() 
    127     print ("   CPU info:") 
    128     try: 
    129         lscpu_path = run_command (('which', 'lscpu')).rstrip () 
    130         print (run_command_string ((lscpu_path,))) 
    131     except subprocess.CalledProcessError: 
    132         with open ('/proc/cpuinfo') as f: 
    133             for l in f: 
    134                 print (l, end='') 
    135  
    136     print (" Configuration...") 
    137     # check RT settings 
    138  
    139     # check IRQ settings 
    140     print ("  IRQ information") 
    141     info = listirqinfo.IRQInfo() 
    142     info.load() 
    143     info.display() 
    144  
    145     print ("") 
    146     print ("=== REPORT ===") 
    147  
    148     # do the interpretation of the tests 
    149     print ("FireWire kernel drivers:") 
    150     if (oldstack_loaded or oldstack_statically_linked) and \ 
    151        (newstack_loaded or newstack_statically_linked): 
    152         print (helpstrings.MODULES_BOTH_STACKS_LOADED) 
    153         sys.exit(-1) 
    154     elif newstack_loaded or newstack_statically_linked: 
    155         print (helpstrings.MODULES_NEW_STACK_LOADED) 
    156         sys.exit(-1) 
    157     elif oldstack_statically_linked: 
    158         print ("[PASS] Kernel drivers statically linked into the kernel.") 
    159     elif not oldstack_present: 
    160         print (helpstrings.MODULES_OLD_STACK_NOT_INSTALLED) 
    161         sys.exit(-1) 
    162     elif not oldstack_loaded: 
    163         print (helpstrings.MODULES_OLD_STACK_NOT_LOADED) 
    164         sys.exit(-1) 
     53        print (stderr) 
     54        sys.exit (1) 
     55 
     56# Parse command line. 
     57usage = """Usage: ffado-diag [--static | -V | --version | --usage] 
     58  --static       Only display executable paths and libraries. 
     59  -V, --version  Display version information. 
     60  --usage        Print a short usage message and exit.""" 
     61if len (sys.argv) == 1: 
     62    static_option = False 
     63elif len (sys.argv) == 2: 
     64    if sys.argv [1] == "--static": 
     65        static_option = True 
     66    elif sys.argv [1] in ("-V", "--version"): 
     67        print (version_info) 
     68        sys.exit (0) 
     69    elif sys.argv [1] == "--usage": 
     70        print (usage) 
     71        sys.exit (0) 
    16572    else: 
    166         print ("[PASS] Kernel modules present and correctly loaded.") 
    167  
    168     if not devnode_present: 
    169         print (helpstrings.DEVNODE_OLD_STACK_NOT_PRESENT) 
    170         sys.exit(-1) 
    171     else: 
    172         if not devnode_permissions: 
    173             print (helpstrings.DEVNODE_OLD_STACK_NO_PERMISSION) 
    174             sys.exit(-1) 
     73        print (usage) 
     74        sys.exit (1) 
     75else: 
     76    print (usage) 
     77    sys.exit (1) 
     78 
     79if not(static_option): 
     80    print (version_info) 
     81    print (copyright_info) 
     82 
     83for command in ("gcc", "g++", "pyuic4", "pyuic5"): 
     84    path = which (command) 
     85    show_pair (command, path) 
     86    if path: 
     87        version = stdout (path, "--version") 
     88        show_pair ('', version [:version.find ("\n")]) 
     89 
     90# jackd --version exits with a non-zero status (tested with jackd 1.9.10). 
     91path = which ("jackd") 
     92show_pair ("jackd", path) 
     93if path: 
     94    popen = subprocess.Popen ((path, "--version"), stdout=subprocess.PIPE) 
     95    version, _ = popen.communicate () 
     96    version = version.decode ("utf8") 
     97    show_pair ('', version [:version.find ("\n")]) 
     98 
     99pkg_config = which ("pkg-config") 
     100show_pair ("pkg-config", pkg_config) 
     101 
     102if pkg_config: 
     103    for lib in ("jack", "libraw1394", "libavc1394", "libiec61883", "libxml++-2.6", "dbus-1"): 
     104        if subprocess.call ((pkg_config, "--exists", lib)): 
     105            show_pair (lib, "not found") 
    175106        else: 
    176             print ("[PASS] /dev/raw1394 node present and accessible.") 
     107            show_pair (lib, stdout (pkg_config, "--modversion", lib)) 
     108            show_pair ('',  stdout (pkg_config, "--cflags", "--libs", lib)) 
     109 
     110# If the "static" command line argument has been given, stop here. 
     111# Else, attempt to display static_info.txt and go on. 
     112if static_option: 
     113    sys.exit (0) 
     114 
     115print ('') 
     116show_pair ("Build time info", static_info) 
     117try: 
     118    with open (static_info, "r" ) as f: 
     119        for line in f: 
     120            indent (line) 
     121except: 
     122    indent ("Failed to read build time info.") 
     123 
     124print ('') 
     125kernel_version = stdout ("uname", "-r").rstrip () 
     126show_pair ("kernel version", kernel_version) 
     127 
     128uname_v = stdout ("uname", "-v") 
     129show_pair ("Preempt (low latency)", " PREEMPT " in uname_v and not " RT " in uname_v) 
     130show_pair ("RT patched", "PREEMPT RT" in uname_v) 
     131# Hint: 
     132# The main parts of the rt patches are in mainline-kernels nowadays. 
     133# Performance with stock kernels is sufficient... 
     134 
     135fw_devices = glob.glob ("/dev/fw*") 
     136show_pair ("/dev/fw*", fw_devices) 
     137if fw_devices: 
     138    indent (stdout (*(["ls", "-lh"] + fw_devices))) 
     139 
     140show_pair ("User IDs", stdout ("id")) 
     141 
     142show_pair ("uname -a", stdout ("uname", "-a")) 
     143 
     144lspci = which ("lspci") 
     145if not lspci and os.path.exists ("/sbin/lspci"): 
     146    lspci = "/sbin/lspci" 
     147show_pair ("lspci", lspci) 
     148if lspci: 
     149    for m in re.findall ("^\([^ ]*\).*1394.*", stdout (lspci), re.MULTILINE): 
     150        indent (stdout (lspci, "-vv", "-nn", "-s", m.group (1))) 
     151 
     152lscpu = which ("lscpu") 
     153show_pair ("lscpu", lscpu) 
     154if lscpu: 
     155    indent (stdout (lscpu)) 
     156else: 
     157    print ("/proc/cpuinfo") 
     158    with open ("/proc/cpuinfo") as f: 
     159        for l in f: 
     160            indent (l) 
     161 
     162###################################################################### 
     163class IRQ: 
     164    def __init__(self, number): 
     165        self.number              = number 
     166        self.pid                 = "" 
     167        self.scheduling_class    = "" 
     168        self.scheduling_priority = "" 
     169        self.drivers             = "" 
     170        self.cpu_counts          = "" 
     171    def description (self): 
     172        return "IRQ{:>4} PID{:>5} count{:>18} Sched{:>4} priority{:>4} drivers {}"\ 
     173            .format (self.number, self.pid, self.cpu_counts, self.scheduling_class, 
     174                     self.scheduling_priority, self.drivers) 
     175 
     176class SoftIRQ: 
     177    def __init__(self, pid, scheduling_class, scheduling_priority, fullname): 
     178        self.pid                 = pid 
     179        self.fullname            = fullname 
     180        self.scheduling_class    = scheduling_class 
     181        self.scheduling_priority = scheduling_priority 
     182    def name (self): 
     183        return "{}-{}".format (self.fullname, self.pid) 
     184    def description (self): 
     185        return "SoftIRQ{:>12} PID{:>6} Sched{:>4} priority{:>4}) name softirq-{}"\ 
     186            .format (self.name (), self.pid, self.scheduling_class, 
     187                     self.scheduling_priority, self.fullname) 
     188 
     189# get PID info 
     190outtext = stdout ("ps", "-eLo", "pid,cmd,class,rtprio") 
     191 
     192softIRQs = {} 
     193for m in re.findall (r"^([0-9]+) +\[softirq-(.*)\] +([A-Z]+) +([-0-9]+)", outtext, re.MULTILINE): 
     194    irq = SoftIRQ (pid                 = m.group (1), 
     195                   fullname            = m.group (2), 
     196                   scheduling_class    = m.group (3), 
     197                   scheduling_priority = m.group (4)) 
     198    softIRQs [irq.name ()] = irq 
     199 
     200IRQs = {} 
     201for m in re.findall (r"^([0-9]+) +\[IRQ-([0-9]+)\] +([A-Z]{2}) +([-0-9]+)", outtext, re.MULTILINE): 
     202    irq = IRQ (number = int (m.group (2))) 
     203    IRQs [irq.number] = irq 
     204    irq.pid = m.group (1) 
     205    irq.scheduling_class    = m.group (3) 
     206    irq.scheduling_priority = m.group (4) 
     207 
     208# get irq info 
     209regex_irq = re.compile (r"^ *([0-9]+): *((?:[0-9]+ +)+)(.*)$") 
     210with open ("/proc/interrupts") as f: 
     211    for line in f: 
     212        m = regex_irq.search (line) 
     213        if m: 
     214            irq_number = int (m.group(1)) 
     215            if irq_number in IRQs: 
     216                irq = IRQs [irq_number] 
     217            else: 
     218                irq = IRQ (number = irq_number) 
     219                IRQs [irq_number] = irq 
     220            irq.cpu_counts = ",".join (m.group (2).split ()) 
     221            irq.drivers    = ",".join (m.group (3).split ()) 
     222 
     223print ("\nHardware interrupts") 
     224for _, irq in sorted (IRQs.items ()): 
     225    indent (irq.description ()) 
     226print ("\nSoftware interrupts") 
     227for _, irq in sorted (softIRQs.items ()): 
     228    indent (irq.description ()) 
     229print ('') 
     230 
     231###################################################################### 
     232 
     233def module_loaded (module_name, procfile): 
     234    with open (procfile) as f: 
     235        for l in f: 
     236            if module_name in l or module_name.replace ("-", "_") in l: 
     237                return True 
     238    return False 
     239 
     240module_dir = "/lib/modules/" + kernel_version 
     241show_pair ("module directory", module_dir) 
     242 
     243class Stack: 
     244    def __init__ (self, modules, main_module): 
     245        self.present = True 
     246        self.loaded  = True 
     247        for module_name in modules: 
     248            if module_name in stdout ("find", module_dir, "-name", module_name + ".ko"): 
     249                indent (module_name + " present") 
     250            else: 
     251                indent (module_name + " not present") 
     252                self.present = False 
     253 
     254            if module_loaded (module_name, "/proc/modules"): 
     255                indent (module_name + " loaded") 
     256            else: 
     257                indent (module_name + " not loaded") 
     258                self.loaded  = False 
     259 
     260        self.active = self.loaded and module_loaded (main_module, "/proc/interrupts") 
     261        show_pair ("stack active", self.active) 
     262 
     263        self.statically_linked = not self.loaded and os.access ("/sys/module/" + main_module, os.F_OK) 
     264        show_pair ("statically linked", self.statically_linked) 
     265 
     266print ("Old 1394 stack") 
     267oldstack = Stack (("ieee1394", "ohci1394", "raw1394"), "ohci1394") 
     268 
     269print ("New 1394 stack") 
     270newstack = Stack (("firewire-core", "firewire-ohci"), "firewire-ohci") 
     271 
     272print ("Kernel support:") 
     273if (oldstack.loaded or oldstack.statically_linked) and \ 
     274   (newstack.loaded or newstack.statically_linked): 
     275    indent ("""Both old and new FireWire kernel modules are loaded, your system 
     276configuration is bogus.""") 
     277    sys.exit (1) 
     278elif newstack.loaded or newstack.statically_linked: 
     279    indent ("""The new FireWire kernel stack is loaded. 
     280If running a kernel earlier than 2.6.37 and problems are experienced, either 
     281try with the old Firewire kernel stack or upgrade to a newer kernel 
     282(preferrably 2.6.37 or later).""") 
     283    sys.exit (1) 
     284elif oldstack.statically_linked: 
     285    indent ("[PASS] Kernel drivers statically linked into the kernel.") 
     286elif not oldstack.present: 
     287    indent ("""FireWire kernel module(s) not found. 
     288Please ensure that the raw1394 module is loaded.""") 
     289    sys.exit (1) 
     290elif not oldstack.loaded: 
     291    indent ("""FireWire kernel stack not present. 
     292 Please compile the kernel with FireWire support.""") 
     293    sys.exit (1) 
     294else: 
     295    indent ("[PASS] Kernel modules present and correctly loaded.") 
     296 
     297###################################################################### 
     298print ("/dev/raw1394 devices:") 
     299 
     300if not os.path.exists ("/dev/raw1394"): 
     301    indent ("""/dev/raw1394 device node not present. 
     302Please fix your udev configuration or add it as a static node.""") 
     303    sys.exit (1) 
     304 
     305try: 
     306    with open ("/dev/raw1394", "w"): 
     307        pass 
     308except: 
     309    indent ("""Not enough permissions to access /dev/raw1394 device. 
     310Please fix your udev configuration, the node permissions or 
     311the user/group permissions.""") 
     312    sys.exit (1) 
     313 
     314indent ("[PASS] /dev/raw1394 node present and accessible.") 
  • trunk/libffado/support/tools/SConscript

    r2749 r2751  
    4747e['pythondir'] = Template( os.path.join( e['sharedir'], 'python' ) ).safe_substitute( e ) 
    4848 
    49 e.Command( "static_info.txt", "#/SConstruct", "$PYTHON_INTERPRETER support/tools/ffado-diag-static > $TARGET" ) 
     49e.Command( "static_info.txt", "ffado-diag", "support/tools/ffado-diag --static > $TARGET" ) 
    5050 
    5151e.ScanReplace( "ffado-diag.in" ) 
    5252 
    5353e.Install( "$bindir", "ffado-diag" ) 
    54 e.Install( "$pythondir", "helpstrings.py" ) 
    55 e.Install( "$pythondir", "listirqinfo.py" ) 
    5654e.Install( "$pythondir", "static_info.txt" ) 
    57 e.Install( "$pythondir", "ffado_diag_helpers.py" ) 
    5855 
    5956if env['ENABLE_DICE']: