]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/dpdk/usertools/dpdk-pmdinfo.py
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / seastar / dpdk / usertools / dpdk-pmdinfo.py
1 #!/usr/bin/env python
2
3 # -------------------------------------------------------------------------
4 #
5 # Utility to dump PMD_INFO_STRING support from an object file
6 #
7 # -------------------------------------------------------------------------
8 from __future__ import print_function
9 import json
10 import os
11 import platform
12 import string
13 import sys
14 from elftools.common.exceptions import ELFError
15 from elftools.common.py3compat import (byte2int, bytes2str, str2bytes)
16 from elftools.elf.elffile import ELFFile
17 from optparse import OptionParser
18
19 # For running from development directory. It should take precedence over the
20 # installed pyelftools.
21 sys.path.insert(0, '.')
22
23 raw_output = False
24 pcidb = None
25
26 # ===========================================
27
28
29 class Vendor:
30 """
31 Class for vendors. This is the top level class
32 for the devices belong to a specific vendor.
33 self.devices is the device dictionary
34 subdevices are in each device.
35 """
36
37 def __init__(self, vendorStr):
38 """
39 Class initializes with the raw line from pci.ids
40 Parsing takes place inside __init__
41 """
42 self.ID = vendorStr.split()[0]
43 self.name = vendorStr.replace("%s " % self.ID, "").rstrip()
44 self.devices = {}
45
46 def addDevice(self, deviceStr):
47 """
48 Adds a device to self.devices
49 takes the raw line from pci.ids
50 """
51 s = deviceStr.strip()
52 devID = s.split()[0]
53 if devID in self.devices:
54 pass
55 else:
56 self.devices[devID] = Device(deviceStr)
57
58 def report(self):
59 print(self.ID, self.name)
60 for id, dev in self.devices.items():
61 dev.report()
62
63 def find_device(self, devid):
64 # convert to a hex string and remove 0x
65 devid = hex(devid)[2:]
66 try:
67 return self.devices[devid]
68 except:
69 return Device("%s Unknown Device" % devid)
70
71
72 class Device:
73
74 def __init__(self, deviceStr):
75 """
76 Class for each device.
77 Each vendor has its own devices dictionary.
78 """
79 s = deviceStr.strip()
80 self.ID = s.split()[0]
81 self.name = s.replace("%s " % self.ID, "")
82 self.subdevices = {}
83
84 def report(self):
85 print("\t%s\t%s" % (self.ID, self.name))
86 for subID, subdev in self.subdevices.items():
87 subdev.report()
88
89 def addSubDevice(self, subDeviceStr):
90 """
91 Adds a subvendor, subdevice to device.
92 Uses raw line from pci.ids
93 """
94 s = subDeviceStr.strip()
95 spl = s.split()
96 subVendorID = spl[0]
97 subDeviceID = spl[1]
98 subDeviceName = s.split(" ")[-1]
99 devID = "%s:%s" % (subVendorID, subDeviceID)
100 self.subdevices[devID] = SubDevice(
101 subVendorID, subDeviceID, subDeviceName)
102
103 def find_subid(self, subven, subdev):
104 subven = hex(subven)[2:]
105 subdev = hex(subdev)[2:]
106 devid = "%s:%s" % (subven, subdev)
107
108 try:
109 return self.subdevices[devid]
110 except:
111 if (subven == "ffff" and subdev == "ffff"):
112 return SubDevice("ffff", "ffff", "(All Subdevices)")
113 else:
114 return SubDevice(subven, subdev, "(Unknown Subdevice)")
115
116
117 class SubDevice:
118 """
119 Class for subdevices.
120 """
121
122 def __init__(self, vendor, device, name):
123 """
124 Class initializes with vendorid, deviceid and name
125 """
126 self.vendorID = vendor
127 self.deviceID = device
128 self.name = name
129
130 def report(self):
131 print("\t\t%s\t%s\t%s" % (self.vendorID, self.deviceID, self.name))
132
133
134 class PCIIds:
135 """
136 Top class for all pci.ids entries.
137 All queries will be asked to this class.
138 PCIIds.vendors["0e11"].devices["0046"].\
139 subdevices["0e11:4091"].name = "Smart Array 6i"
140 """
141
142 def __init__(self, filename):
143 """
144 Prepares the directories.
145 Checks local data file.
146 Tries to load from local, if not found, downloads from web
147 """
148 self.version = ""
149 self.date = ""
150 self.vendors = {}
151 self.contents = None
152 self.readLocal(filename)
153 self.parse()
154
155 def reportVendors(self):
156 """Reports the vendors
157 """
158 for vid, v in self.vendors.items():
159 print(v.ID, v.name)
160
161 def report(self, vendor=None):
162 """
163 Reports everything for all vendors or a specific vendor
164 PCIIds.report() reports everything
165 PCIIDs.report("0e11") reports only "Compaq Computer Corporation"
166 """
167 if vendor is not None:
168 self.vendors[vendor].report()
169 else:
170 for vID, v in self.vendors.items():
171 v.report()
172
173 def find_vendor(self, vid):
174 # convert vid to a hex string and remove the 0x
175 vid = hex(vid)[2:]
176
177 try:
178 return self.vendors[vid]
179 except:
180 return Vendor("%s Unknown Vendor" % (vid))
181
182 def findDate(self, content):
183 for l in content:
184 if l.find("Date:") > -1:
185 return l.split()[-2].replace("-", "")
186 return None
187
188 def parse(self):
189 if len(self.contents) < 1:
190 print("data/%s-pci.ids not found" % self.date)
191 else:
192 vendorID = ""
193 deviceID = ""
194 for l in self.contents:
195 if l[0] == "#":
196 continue
197 elif len(l.strip()) == 0:
198 continue
199 else:
200 if l.find("\t\t") == 0:
201 self.vendors[vendorID].devices[
202 deviceID].addSubDevice(l)
203 elif l.find("\t") == 0:
204 deviceID = l.strip().split()[0]
205 self.vendors[vendorID].addDevice(l)
206 else:
207 vendorID = l.split()[0]
208 self.vendors[vendorID] = Vendor(l)
209
210 def readLocal(self, filename):
211 """
212 Reads the local file
213 """
214 self.contents = open(filename).readlines()
215 self.date = self.findDate(self.contents)
216
217 def loadLocal(self):
218 """
219 Loads database from local. If there is no file,
220 it creates a new one from web
221 """
222 self.date = idsfile[0].split("/")[1].split("-")[0]
223 self.readLocal()
224
225
226 # =======================================
227
228 def search_file(filename, search_path):
229 """ Given a search path, find file with requested name """
230 for path in string.split(search_path, ":"):
231 candidate = os.path.join(path, filename)
232 if os.path.exists(candidate):
233 return os.path.abspath(candidate)
234 return None
235
236
237 class ReadElf(object):
238 """ display_* methods are used to emit output into the output stream
239 """
240
241 def __init__(self, file, output):
242 """ file:
243 stream object with the ELF file to read
244
245 output:
246 output stream to write to
247 """
248 self.elffile = ELFFile(file)
249 self.output = output
250
251 # Lazily initialized if a debug dump is requested
252 self._dwarfinfo = None
253
254 self._versioninfo = None
255
256 def _section_from_spec(self, spec):
257 """ Retrieve a section given a "spec" (either number or name).
258 Return None if no such section exists in the file.
259 """
260 try:
261 num = int(spec)
262 if num < self.elffile.num_sections():
263 return self.elffile.get_section(num)
264 else:
265 return None
266 except ValueError:
267 # Not a number. Must be a name then
268 return self.elffile.get_section_by_name(str2bytes(spec))
269
270 def pretty_print_pmdinfo(self, pmdinfo):
271 global pcidb
272
273 for i in pmdinfo["pci_ids"]:
274 vendor = pcidb.find_vendor(i[0])
275 device = vendor.find_device(i[1])
276 subdev = device.find_subid(i[2], i[3])
277 print("%s (%s) : %s (%s) %s" %
278 (vendor.name, vendor.ID, device.name,
279 device.ID, subdev.name))
280
281 def parse_pmd_info_string(self, mystring):
282 global raw_output
283 global pcidb
284
285 optional_pmd_info = [
286 {'id': 'params', 'tag': 'PMD PARAMETERS'},
287 {'id': 'kmod', 'tag': 'PMD KMOD DEPENDENCIES'}
288 ]
289
290 i = mystring.index("=")
291 mystring = mystring[i + 2:]
292 pmdinfo = json.loads(mystring)
293
294 if raw_output:
295 print(json.dumps(pmdinfo))
296 return
297
298 print("PMD NAME: " + pmdinfo["name"])
299 for i in optional_pmd_info:
300 try:
301 print("%s: %s" % (i['tag'], pmdinfo[i['id']]))
302 except KeyError:
303 continue
304
305 if (len(pmdinfo["pci_ids"]) != 0):
306 print("PMD HW SUPPORT:")
307 if pcidb is not None:
308 self.pretty_print_pmdinfo(pmdinfo)
309 else:
310 print("VENDOR\t DEVICE\t SUBVENDOR\t SUBDEVICE")
311 for i in pmdinfo["pci_ids"]:
312 print("0x%04x\t 0x%04x\t 0x%04x\t\t 0x%04x" %
313 (i[0], i[1], i[2], i[3]))
314
315 print("")
316
317 def display_pmd_info_strings(self, section_spec):
318 """ Display a strings dump of a section. section_spec is either a
319 section number or a name.
320 """
321 section = self._section_from_spec(section_spec)
322 if section is None:
323 return
324
325 data = section.data()
326 dataptr = 0
327
328 while dataptr < len(data):
329 while (dataptr < len(data) and
330 not (32 <= byte2int(data[dataptr]) <= 127)):
331 dataptr += 1
332
333 if dataptr >= len(data):
334 break
335
336 endptr = dataptr
337 while endptr < len(data) and byte2int(data[endptr]) != 0:
338 endptr += 1
339
340 mystring = bytes2str(data[dataptr:endptr])
341 rc = mystring.find("PMD_INFO_STRING")
342 if (rc != -1):
343 self.parse_pmd_info_string(mystring)
344
345 dataptr = endptr
346
347 def find_librte_eal(self, section):
348 for tag in section.iter_tags():
349 if tag.entry.d_tag == 'DT_NEEDED':
350 if "librte_eal" in tag.needed:
351 return tag.needed
352 return None
353
354 def search_for_autoload_path(self):
355 scanelf = self
356 scanfile = None
357 library = None
358
359 section = self._section_from_spec(".dynamic")
360 try:
361 eallib = self.find_librte_eal(section)
362 if eallib is not None:
363 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
364 if ldlibpath is None:
365 ldlibpath = ""
366 dtr = self.get_dt_runpath(section)
367 library = search_file(eallib,
368 dtr + ":" + ldlibpath +
369 ":/usr/lib64:/lib64:/usr/lib:/lib")
370 if library is None:
371 return (None, None)
372 if raw_output is False:
373 print("Scanning for autoload path in %s" % library)
374 scanfile = open(library, 'rb')
375 scanelf = ReadElf(scanfile, sys.stdout)
376 except AttributeError:
377 # Not a dynamic binary
378 pass
379 except ELFError:
380 scanfile.close()
381 return (None, None)
382
383 section = scanelf._section_from_spec(".rodata")
384 if section is None:
385 if scanfile is not None:
386 scanfile.close()
387 return (None, None)
388
389 data = section.data()
390 dataptr = 0
391
392 while dataptr < len(data):
393 while (dataptr < len(data) and
394 not (32 <= byte2int(data[dataptr]) <= 127)):
395 dataptr += 1
396
397 if dataptr >= len(data):
398 break
399
400 endptr = dataptr
401 while endptr < len(data) and byte2int(data[endptr]) != 0:
402 endptr += 1
403
404 mystring = bytes2str(data[dataptr:endptr])
405 rc = mystring.find("DPDK_PLUGIN_PATH")
406 if (rc != -1):
407 rc = mystring.find("=")
408 return (mystring[rc + 1:], library)
409
410 dataptr = endptr
411 if scanfile is not None:
412 scanfile.close()
413 return (None, None)
414
415 def get_dt_runpath(self, dynsec):
416 for tag in dynsec.iter_tags():
417 if tag.entry.d_tag == 'DT_RUNPATH':
418 return tag.runpath
419 return ""
420
421 def process_dt_needed_entries(self):
422 """ Look to see if there are any DT_NEEDED entries in the binary
423 And process those if there are
424 """
425 global raw_output
426 runpath = ""
427 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
428 if ldlibpath is None:
429 ldlibpath = ""
430
431 dynsec = self._section_from_spec(".dynamic")
432 try:
433 runpath = self.get_dt_runpath(dynsec)
434 except AttributeError:
435 # dynsec is None, just return
436 return
437
438 for tag in dynsec.iter_tags():
439 if tag.entry.d_tag == 'DT_NEEDED':
440 rc = tag.needed.find(b"librte_pmd")
441 if (rc != -1):
442 library = search_file(tag.needed,
443 runpath + ":" + ldlibpath +
444 ":/usr/lib64:/lib64:/usr/lib:/lib")
445 if library is not None:
446 if raw_output is False:
447 print("Scanning %s for pmd information" % library)
448 with open(library, 'rb') as file:
449 try:
450 libelf = ReadElf(file, sys.stdout)
451 except ELFError:
452 print("%s is no an ELF file" % library)
453 continue
454 libelf.process_dt_needed_entries()
455 libelf.display_pmd_info_strings(".rodata")
456 file.close()
457
458
459 def scan_autoload_path(autoload_path):
460 global raw_output
461
462 if os.path.exists(autoload_path) is False:
463 return
464
465 try:
466 dirs = os.listdir(autoload_path)
467 except OSError:
468 # Couldn't read the directory, give up
469 return
470
471 for d in dirs:
472 dpath = os.path.join(autoload_path, d)
473 if os.path.isdir(dpath):
474 scan_autoload_path(dpath)
475 if os.path.isfile(dpath):
476 try:
477 file = open(dpath, 'rb')
478 readelf = ReadElf(file, sys.stdout)
479 except ELFError:
480 # this is likely not an elf file, skip it
481 continue
482 except IOError:
483 # No permission to read the file, skip it
484 continue
485
486 if raw_output is False:
487 print("Hw Support for library %s" % d)
488 readelf.display_pmd_info_strings(".rodata")
489 file.close()
490
491
492 def scan_for_autoload_pmds(dpdk_path):
493 """
494 search the specified application or path for a pmd autoload path
495 then scan said path for pmds and report hw support
496 """
497 global raw_output
498
499 if (os.path.isfile(dpdk_path) is False):
500 if raw_output is False:
501 print("Must specify a file name")
502 return
503
504 file = open(dpdk_path, 'rb')
505 try:
506 readelf = ReadElf(file, sys.stdout)
507 except ElfError:
508 if raw_output is False:
509 print("Unable to parse %s" % file)
510 return
511
512 (autoload_path, scannedfile) = readelf.search_for_autoload_path()
513 if (autoload_path is None or autoload_path is ""):
514 if (raw_output is False):
515 print("No autoload path configured in %s" % dpdk_path)
516 return
517 if (raw_output is False):
518 if (scannedfile is None):
519 scannedfile = dpdk_path
520 print("Found autoload path %s in %s" % (autoload_path, scannedfile))
521
522 file.close()
523 if (raw_output is False):
524 print("Discovered Autoload HW Support:")
525 scan_autoload_path(autoload_path)
526 return
527
528
529 def main(stream=None):
530 global raw_output
531 global pcidb
532
533 pcifile_default = "./pci.ids" # For unknown OS's assume local file
534 if platform.system() == 'Linux':
535 pcifile_default = "/usr/share/hwdata/pci.ids"
536 elif platform.system() == 'FreeBSD':
537 pcifile_default = "/usr/local/share/pciids/pci.ids"
538 if not os.path.exists(pcifile_default):
539 pcifile_default = "/usr/share/misc/pci_vendors"
540
541 optparser = OptionParser(
542 usage='usage: %prog [-hrtp] [-d <pci id file] <elf-file>',
543 description="Dump pmd hardware support info",
544 add_help_option=True)
545 optparser.add_option('-r', '--raw',
546 action='store_true', dest='raw_output',
547 help='Dump raw json strings')
548 optparser.add_option("-d", "--pcidb", dest="pcifile",
549 help="specify a pci database "
550 "to get vendor names from",
551 default=pcifile_default, metavar="FILE")
552 optparser.add_option("-t", "--table", dest="tblout",
553 help="output information on hw support as a "
554 "hex table",
555 action='store_true')
556 optparser.add_option("-p", "--plugindir", dest="pdir",
557 help="scan dpdk for autoload plugins",
558 action='store_true')
559
560 options, args = optparser.parse_args()
561
562 if options.raw_output:
563 raw_output = True
564
565 if options.pcifile:
566 pcidb = PCIIds(options.pcifile)
567 if pcidb is None:
568 print("Pci DB file not found")
569 exit(1)
570
571 if options.tblout:
572 options.pcifile = None
573 pcidb = None
574
575 if (len(args) == 0):
576 optparser.print_usage()
577 exit(1)
578
579 if options.pdir is True:
580 exit(scan_for_autoload_pmds(args[0]))
581
582 ldlibpath = os.environ.get('LD_LIBRARY_PATH')
583 if (ldlibpath is None):
584 ldlibpath = ""
585
586 if (os.path.exists(args[0]) is True):
587 myelffile = args[0]
588 else:
589 myelffile = search_file(
590 args[0], ldlibpath + ":/usr/lib64:/lib64:/usr/lib:/lib")
591
592 if (myelffile is None):
593 print("File not found")
594 sys.exit(1)
595
596 with open(myelffile, 'rb') as file:
597 try:
598 readelf = ReadElf(file, sys.stdout)
599 readelf.process_dt_needed_entries()
600 readelf.display_pmd_info_strings(".rodata")
601 sys.exit(0)
602
603 except ELFError as ex:
604 sys.stderr.write('ELF error: %s\n' % ex)
605 sys.exit(1)
606
607
608 # -------------------------------------------------------------------------
609 if __name__ == '__main__':
610 main()