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