root/trunk/libffado/support/tools/ffado-diag.in

Revision 2763, 10.6 kB (checked in by jwoithe, 3 years ago)

ffado-diag: correct regex used to find firewire controllers in lspci output.

The previous re.findall() regex expected the PCI bus ID to be within literal
parentheses, while the intent was probably to specify a subexpression (or
"group" as python calls it).

When a subexpression (group) is included in the regex passed to
re.findall(), the values produced re.findall() are the contents of the
subexpression. A call to the group() method is not required.

These changes have been tested on python2.

  • Property svn:executable set to *
Line 
1 #!$PYTHON_INTERPRETER
2 # Dollar variables are preprocessed by SConscript at build time.
3 version_info = "FFADO diagnostic utility $VERSION$REVISIONSTRING"
4 copyright_info = """
5 (C) 2008 Pieter Palmers
6     2009-2010 Arnold Krille
7     2018 Nicolas Boulenguez, Jonathan Woithe
8 """
9 static_info = "$LIBDATADIR/static_info.txt"
10 # This program is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, version 3 of the License.
13
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18
19 # You should have received a copy of the GNU General Public License
20 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
21
22 # Test for common FFADO problems
23
24 import glob
25 import os.path
26 import re
27 import subprocess
28 import sys
29
30 # Prefer reproducible output with few non-ASCII characters.
31 os.environ ["LC_ALL"] = "C"
32
33 # Consistent formatting.
34 def show_pair (key, value):
35     # rstrip () for convenience, but do not assume that value is a string.
36     print ("{:25} {}".format (key, value).rstrip ())
37 def indent (lines):
38     print ("  {}".format (lines.rstrip ().replace ("\n", "\n  ")))
39
40 # Convenient shortcuts.
41 def stdout (*args):
42     return subprocess.check_output (args).decode ("utf8")
43
44 def 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
52     else:
53         print (stderr)
54         sys.exit (1)
55
56 # Parse command line.
57 usage = """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."""
61 if len (sys.argv) == 1:
62     static_option = False
63 elif 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)
72     else:
73         print (usage)
74         sys.exit (1)
75 else:
76     print (usage)
77     sys.exit (1)
78
79 if not(static_option):
80     print (version_info)
81     print (copyright_info)
82
83 for 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).
91 path = which ("jackd")
92 show_pair ("jackd", path)
93 if 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
99 pkg_config = which ("pkg-config")
100 show_pair ("pkg-config", pkg_config)
101
102 if 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")
106         else:
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.
112 if static_option:
113     sys.exit (0)
114
115 print ('')
116 show_pair ("Build time info", static_info)
117 try:
118     with open (static_info, "r" ) as f:
119         for line in f:
120             indent (line)
121 except:
122     indent ("Failed to read build time info.")
123
124 print ('')
125 kernel_version = stdout ("uname", "-r").rstrip ()
126 show_pair ("kernel version", kernel_version)
127
128 uname_v = stdout ("uname", "-v")
129 show_pair ("Preempt (low latency)", " PREEMPT " in uname_v and not " RT " in uname_v)
130 show_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
135 fw_devices = glob.glob ("/dev/fw*")
136 show_pair ("/dev/fw*", fw_devices)
137 if fw_devices:
138     indent (stdout (*(["ls", "-lh"] + fw_devices)))
139
140 show_pair ("User IDs", stdout ("id"))
141
142 show_pair ("uname -a", stdout ("uname", "-a"))
143
144 lspci = which ("lspci")
145 if not lspci and os.path.exists ("/sbin/lspci"):
146     lspci = "/sbin/lspci"
147 show_pair ("lspci", lspci)
148 if lspci:
149     for m in re.findall ("^([^ ]*).*1394.*", stdout (lspci), re.MULTILINE):
150         indent (stdout (lspci, "-vv", "-nn", "-s", m))
151
152 lscpu = which ("lscpu")
153 show_pair ("lscpu", lscpu)
154 if lscpu:
155     indent (stdout (lscpu))
156 else:
157     print ("/proc/cpuinfo")
158     with open ("/proc/cpuinfo") as f:
159         for l in f:
160             indent (l)
161
162 ######################################################################
163 class 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
176 class 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
190 outtext = stdout ("ps", "-eLo", "pid,cmd,class,rtprio")
191
192 softIRQs = {}
193 for 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
200 IRQs = {}
201 for 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
209 regex_irq = re.compile (r"^ *([0-9]+): *((?:[0-9]+ +)+)(.*)$")
210 with 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
223 print ("\nHardware interrupts")
224 for _, irq in sorted (IRQs.items ()):
225     indent (irq.description ())
226 print ("\nSoftware interrupts")
227 for _, irq in sorted (softIRQs.items ()):
228     indent (irq.description ())
229 print ('')
230
231 ######################################################################
232
233 def 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
240 module_dir = "/lib/modules/" + kernel_version
241 show_pair ("module directory", module_dir)
242
243 class 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
266 print ("Old 1394 stack")
267 oldstack = Stack (("ieee1394", "ohci1394", "raw1394"), "ohci1394")
268
269 print ("New 1394 stack")
270 newstack = Stack (("firewire-core", "firewire-ohci"), "firewire-ohci")
271
272 print ("Kernel support:")
273 if (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
276 configuration is bogus.""")
277     sys.exit (1)
278 elif newstack.loaded or newstack.statically_linked:
279     indent ("""The new FireWire kernel stack is loaded.
280 If running a kernel earlier than 2.6.37 and problems are experienced, either
281 try with the old Firewire kernel stack or upgrade to a newer kernel
282 (preferrably 2.6.37 or later).""")
283     sys.exit (1)
284 elif oldstack.statically_linked:
285     indent ("[PASS] Kernel drivers statically linked into the kernel.")
286 elif not oldstack.present:
287     indent ("""FireWire kernel module(s) not found.
288 Please ensure that the raw1394 module is loaded.""")
289     sys.exit (1)
290 elif not oldstack.loaded:
291     indent ("""FireWire kernel stack not present.
292  Please compile the kernel with FireWire support.""")
293     sys.exit (1)
294 else:
295     indent ("[PASS] Kernel modules present and correctly loaded.")
296
297 ######################################################################
298 print ("/dev/raw1394 devices:")
299
300 if not os.path.exists ("/dev/raw1394"):
301     indent ("""/dev/raw1394 device node not present.
302 Please fix your udev configuration or add it as a static node.""")
303     sys.exit (1)
304
305 try:
306     with open ("/dev/raw1394", "w"):
307         pass
308 except:
309     indent ("""Not enough permissions to access /dev/raw1394 device.
310 Please fix your udev configuration, the node permissions or
311 the user/group permissions.""")
312     sys.exit (1)
313
314 indent ("[PASS] /dev/raw1394 node present and accessible.")
Note: See TracBrowser for help on using the browser.