summaryrefslogtreecommitdiffstats
path: root/tools/symalyzer.py
diff options
context:
space:
mode:
authorwhitespace <nobody@nobody>2020-10-07 23:22:26 +0200
committerQuentin Young <qlyoung@nvidia.com>2020-10-07 23:22:26 +0200
commit701a01920eee5431d2052aad92aefbdf50ac2139 (patch)
tree2bf2339327241f59593b9583b060ebb347db1cea /tools/symalyzer.py
parentMerge pull request #7058 from Niral-Networks/niral_dev_vrf_ospf6 (diff)
downloadfrr-701a01920eee5431d2052aad92aefbdf50ac2139.tar.xz
frr-701a01920eee5431d2052aad92aefbdf50ac2139.zip
*: reformat python files
We are now using black. Signed-off-by: Quentin Young <qlyoung@nvidia.com>
Diffstat (limited to 'tools/symalyzer.py')
-rwxr-xr-xtools/symalyzer.py251
1 files changed, 157 insertions, 94 deletions
diff --git a/tools/symalyzer.py b/tools/symalyzer.py
index cff21f9f9..ce0bfde0a 100755
--- a/tools/symalyzer.py
+++ b/tools/symalyzer.py
@@ -21,18 +21,40 @@ import sys, os, subprocess
import re
from collections import namedtuple
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'python'))
+sys.path.insert(
+ 0,
+ os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "python"),
+)
from makevars import MakeVars
-SymRowBase = namedtuple('SymRow', ['target', 'object', 'name', 'address', 'klass', 'typ', 'size', 'line', 'section', 'loc'])
+SymRowBase = namedtuple(
+ "SymRow",
+ [
+ "target",
+ "object",
+ "name",
+ "address",
+ "klass",
+ "typ",
+ "size",
+ "line",
+ "section",
+ "loc",
+ ],
+)
+
+
class SymRow(SymRowBase):
- '''
+ """
wrapper around a line of `nm` output
- '''
- lib_re = re.compile(r'/lib[^/]+\.(so|la)$')
+ """
+
+ lib_re = re.compile(r"/lib[^/]+\.(so|la)$")
+
def is_global(self):
- return self.klass.isupper() or self.klass in 'uvw'
+ return self.klass.isupper() or self.klass in "uvw"
+
def scope(self):
if self.lib_re.search(self.target) is None:
return self.target
@@ -40,28 +62,29 @@ class SymRow(SymRowBase):
return None
def is_export(self):
- '''
+ """
FRR-specific list of symbols which are considered "externally used"
e.g. hooks are by design APIs for external use, same for qobj_t_*
frr_inet_ntop is here because it's used through an ELF alias to
"inet_ntop()"
- '''
- if self.name in ['main', 'frr_inet_ntop', '_libfrr_version']:
+ """
+ if self.name in ["main", "frr_inet_ntop", "_libfrr_version"]:
return True
- if self.name.startswith('_hook_'):
+ if self.name.startswith("_hook_"):
return True
- if self.name.startswith('qobj_t_'):
+ if self.name.startswith("qobj_t_"):
return True
return False
+
class Symbols(dict):
- '''
+ """
dict of all symbols in all libs & executables
- '''
+ """
- from_re = re.compile(r'^Symbols from (.*?):$')
- lt_re = re.compile(r'^(.*/)([^/]+)\.l[oa]$')
+ from_re = re.compile(r"^Symbols from (.*?):$")
+ lt_re = re.compile(r"^(.*/)([^/]+)\.l[oa]$")
def __init__(self):
super().__init__()
@@ -69,26 +92,35 @@ class Symbols(dict):
class ReportSym(object):
def __init__(self, sym):
self.sym = sym
+
def __repr__(self):
- return '<%-25s %-40s [%s]>' % (self.__class__.__name__ + ':', self.sym.name, self.sym.loc)
+ return "<%-25s %-40s [%s]>" % (
+ self.__class__.__name__ + ":",
+ self.sym.name,
+ self.sym.loc,
+ )
+
def __lt__(self, other):
return self.sym.name.__lt__(other.sym.name)
class ReportSymCouldBeStaticAlreadyLocal(ReportSym):
- idshort = 'Z'
- idlong = 'extrastatic'
+ idshort = "Z"
+ idlong = "extrastatic"
title = "symbol is local to library, but only used in its source file (make static?)"
+
class ReportSymCouldBeStatic(ReportSym):
- idshort = 'S'
- idlong = 'static'
+ idshort = "S"
+ idlong = "static"
title = "symbol is only used in its source file (make static?)"
+
class ReportSymCouldBeLibLocal(ReportSym):
- idshort = 'L'
- idlong = 'liblocal'
+ idshort = "L"
+ idlong = "liblocal"
title = "symbol is only used inside of library"
+
class ReportSymModuleAPI(ReportSym):
- idshort = 'A'
- idlong = 'api'
+ idshort = "A"
+ idlong = "api"
title = "symbol (in executable) is referenced externally from a module"
class Symbol(object):
@@ -100,31 +132,38 @@ class Symbols(dict):
def process(self, row):
scope = row.scope()
- if row.section == '*UND*':
+ if row.section == "*UND*":
self.refs.append(row)
else:
self.defs.setdefault(scope, []).append(row)
def evaluate(self, out):
- '''
+ """
generate output report
invoked after all object files have been read in, so it can look
at inter-object-file relationships
- '''
+ """
if len(self.defs) == 0:
out.extsyms.add(self.name)
return
for scopename, symdefs in self.defs.items():
- common_defs = [symdef for symdef in symdefs if symdef.section == '*COM*']
- proper_defs = [symdef for symdef in symdefs if symdef.section != '*COM*']
+ common_defs = [
+ symdef for symdef in symdefs if symdef.section == "*COM*"
+ ]
+ proper_defs = [
+ symdef for symdef in symdefs if symdef.section != "*COM*"
+ ]
if len(proper_defs) > 1:
- print(self.name, ' DUPLICATE')
- print('\tD: %s %s' % (scopename, '\n\t\t'.join([repr(s) for s in symdefs])))
+ print(self.name, " DUPLICATE")
+ print(
+ "\tD: %s %s"
+ % (scopename, "\n\t\t".join([repr(s) for s in symdefs]))
+ )
for syms in self.refs:
- print('\tR: %s' % (syms, ))
+ print("\tR: %s" % (syms,))
return
if len(proper_defs):
@@ -140,7 +179,9 @@ class Symbols(dict):
if scopename is not None and len(self.refs) > 0:
for ref in self.refs:
- if ref.target != primary_def.target and ref.target.endswith('.la'):
+ if ref.target != primary_def.target and ref.target.endswith(
+ ".la"
+ ):
outobj = out.report.setdefault(primary_def.object, [])
outobj.append(out.ReportSymModuleAPI(primary_def))
break
@@ -152,7 +193,9 @@ class Symbols(dict):
if primary_def.visible:
outobj.append(out.ReportSymCouldBeStatic(primary_def))
else:
- outobj.append(out.ReportSymCouldBeStaticAlreadyLocal(primary_def))
+ outobj.append(
+ out.ReportSymCouldBeStaticAlreadyLocal(primary_def)
+ )
continue
if scopename is None and primary_def.visible:
@@ -164,7 +207,6 @@ class Symbols(dict):
outobj = out.report.setdefault(primary_def.object, [])
outobj.append(out.ReportSymCouldBeLibLocal(primary_def))
-
def evaluate(self):
self.extsyms = set()
self.report = {}
@@ -177,14 +219,14 @@ class Symbols(dict):
m = self.lt_re.match(fn)
if m is None:
return fn
- return m.group(1) + '.libs/' + m.group(2) + '.o'
+ return m.group(1) + ".libs/" + m.group(2) + ".o"
def libtooltargetmustdie(fn):
m = self.lt_re.match(fn)
if m is None:
- a, b = fn.rsplit('/', 1)
- return '%s/.libs/%s' % (a, b)
- return m.group(1) + '.libs/' + m.group(2) + '.so'
+ a, b = fn.rsplit("/", 1)
+ return "%s/.libs/%s" % (a, b)
+ return m.group(1) + ".libs/" + m.group(2) + ".so"
files = list(set([libtoolmustdie(fn) for fn in files]))
@@ -192,30 +234,30 @@ class Symbols(dict):
filename = None
path_rel_to = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- for line in text.split('\n'):
- if line.strip() == '':
+ for line in text.split("\n"):
+ if line.strip() == "":
continue
m = self.from_re.match(line)
if m is not None:
filename = m.group(1)
continue
- if line.startswith('Name'):
+ if line.startswith("Name"):
continue
- items = [i.strip() for i in line.split('|')]
+ items = [i.strip() for i in line.split("|")]
loc = None
- if '\t' in items[-1]:
- items[-1], loc = items[-1].split('\t', 1)
- fn, lno = loc.rsplit(':', 1)
+ if "\t" in items[-1]:
+ items[-1], loc = items[-1].split("\t", 1)
+ fn, lno = loc.rsplit(":", 1)
fn = os.path.relpath(fn, path_rel_to)
- loc = '%s:%s' % (fn, lno)
+ loc = "%s:%s" % (fn, lno)
- items[1] = int(items[1] if items[1] != '' else '0', 16)
- items[4] = int(items[4] if items[4] != '' else '0', 16)
+ items[1] = int(items[1] if items[1] != "" else "0", 16)
+ items[4] = int(items[4] if items[4] != "" else "0", 16)
items.append(loc)
row = SymRow(target, filename, *items)
- if row.section == '.group' or row.name == '_GLOBAL_OFFSET_TABLE_':
+ if row.section == ".group" or row.name == "_GLOBAL_OFFSET_TABLE_":
continue
if not row.is_global():
continue
@@ -230,14 +272,19 @@ class Symbols(dict):
# in the linked result (this covers ELF "hidden"/"internal" linkage)
libfile = libtooltargetmustdie(target)
- nmlib = subprocess.Popen(['nm', '-l', '-g', '--defined-only', '-f', 'sysv', libfile], stdout = subprocess.PIPE)
- out = nmlib.communicate()[0].decode('US-ASCII')
+ nmlib = subprocess.Popen(
+ ["nm", "-l", "-g", "--defined-only", "-f", "sysv", libfile],
+ stdout=subprocess.PIPE,
+ )
+ out = nmlib.communicate()[0].decode("US-ASCII")
for row in parse_nm_output(out):
visible_syms.add(row.name)
- nm = subprocess.Popen(['nm', '-l', '-f', 'sysv'] + files, stdout = subprocess.PIPE)
- out = nm.communicate()[0].decode('US-ASCII')
+ nm = subprocess.Popen(
+ ["nm", "-l", "-f", "sysv"] + files, stdout=subprocess.PIPE
+ )
+ out = nm.communicate()[0].decode("US-ASCII")
for row in parse_nm_output(out):
row.visible = row.name in visible_syms
@@ -249,95 +296,111 @@ def write_html_report(syms):
try:
import jinja2
except ImportError:
- sys.stderr.write('jinja2 could not be imported, not writing HTML report!\n')
+ sys.stderr.write("jinja2 could not be imported, not writing HTML report!\n")
return
self_path = os.path.dirname(os.path.abspath(__file__))
jenv = jinja2.Environment(loader=jinja2.FileSystemLoader(self_path))
- template = jenv.get_template('symalyzer.html')
+ template = jenv.get_template("symalyzer.html")
dirgroups = {}
for fn, reports in syms.report.items():
- dirname, filename = fn.replace('.libs/', '').rsplit('/', 1)
+ dirname, filename = fn.replace(".libs/", "").rsplit("/", 1)
dirgroups.setdefault(dirname, {})[fn] = reports
klasses = {
- 'T': 'code / plain old regular function (Text)',
- 'D': 'global variable, read-write, with nonzero initializer (Data)',
- 'B': 'global variable, read-write, with zero initializer (BSS)',
- 'C': 'global variable, read-write, with zero initializer (Common)',
- 'R': 'global variable, read-only (Rodata)',
+ "T": "code / plain old regular function (Text)",
+ "D": "global variable, read-write, with nonzero initializer (Data)",
+ "B": "global variable, read-write, with zero initializer (BSS)",
+ "C": "global variable, read-write, with zero initializer (Common)",
+ "R": "global variable, read-only (Rodata)",
}
- with open('symalyzer_report.html.tmp', 'w') as fd:
- fd.write(template.render(dirgroups = dirgroups, klasses = klasses))
- os.rename('symalyzer_report.html.tmp', 'symalyzer_report.html')
+ with open("symalyzer_report.html.tmp", "w") as fd:
+ fd.write(template.render(dirgroups=dirgroups, klasses=klasses))
+ os.rename("symalyzer_report.html.tmp", "symalyzer_report.html")
- if not os.path.exists('jquery-3.4.1.min.js'):
- url = 'https://code.jquery.com/jquery-3.4.1.min.js'
+ if not os.path.exists("jquery-3.4.1.min.js"):
+ url = "https://code.jquery.com/jquery-3.4.1.min.js"
sys.stderr.write(
- 'trying to grab a copy of jquery from %s\nif this fails, please get it manually (the HTML output is done.)\n' % (url))
+ "trying to grab a copy of jquery from %s\nif this fails, please get it manually (the HTML output is done.)\n"
+ % (url)
+ )
import requests
- r = requests.get('https://code.jquery.com/jquery-3.4.1.min.js')
+
+ r = requests.get("https://code.jquery.com/jquery-3.4.1.min.js")
if r.status_code != 200:
- sys.stderr.write('failed -- please download jquery-3.4.1.min.js and put it next to the HTML report\n')
+ sys.stderr.write(
+ "failed -- please download jquery-3.4.1.min.js and put it next to the HTML report\n"
+ )
else:
- with open('jquery-3.4.1.min.js.tmp', 'w') as fd:
+ with open("jquery-3.4.1.min.js.tmp", "w") as fd:
fd.write(r.text)
- os.rename('jquery-3.4.1.min.js.tmp', 'jquery-3.4.1.min.js')
- sys.stderr.write('done.\n')
+ os.rename("jquery-3.4.1.min.js.tmp", "jquery-3.4.1.min.js")
+ sys.stderr.write("done.\n")
+
def automake_escape(s):
- return s.replace('.', '_').replace('/', '_')
+ return s.replace(".", "_").replace("/", "_")
-if __name__ == '__main__':
+
+if __name__ == "__main__":
mv = MakeVars()
- if not (os.path.exists('config.version') and os.path.exists('lib/.libs/libfrr.so')):
- sys.stderr.write('please execute this script in the root directory of an FRR build tree\n')
- sys.stderr.write('./configure && make need to have completed successfully\n')
+ if not (os.path.exists("config.version") and os.path.exists("lib/.libs/libfrr.so")):
+ sys.stderr.write(
+ "please execute this script in the root directory of an FRR build tree\n"
+ )
+ sys.stderr.write("./configure && make need to have completed successfully\n")
sys.exit(1)
- amtargets = ['bin_PROGRAMS', 'sbin_PROGRAMS', 'lib_LTLIBRARIES', 'module_LTLIBRARIES']
+ amtargets = [
+ "bin_PROGRAMS",
+ "sbin_PROGRAMS",
+ "lib_LTLIBRARIES",
+ "module_LTLIBRARIES",
+ ]
targets = []
mv.getvars(amtargets)
for amtarget in amtargets:
- targets.extend([item for item in mv[amtarget].strip().split() if item != 'tools/ssd'])
+ targets.extend(
+ [item for item in mv[amtarget].strip().split() if item != "tools/ssd"]
+ )
- mv.getvars(['%s_LDADD' % automake_escape(t) for t in targets])
+ mv.getvars(["%s_LDADD" % automake_escape(t) for t in targets])
ldobjs = targets[:]
for t in targets:
- ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split()
+ ldadd = mv["%s_LDADD" % automake_escape(t)].strip().split()
for item in ldadd:
- if item.startswith('-'):
+ if item.startswith("-"):
continue
- if item.endswith('.a'):
+ if item.endswith(".a"):
ldobjs.append(item)
- mv.getvars(['%s_OBJECTS' % automake_escape(o) for o in ldobjs])
+ mv.getvars(["%s_OBJECTS" % automake_escape(o) for o in ldobjs])
syms = Symbols()
for t in targets:
- objs = mv['%s_OBJECTS' % automake_escape(t)].strip().split()
- ldadd = mv['%s_LDADD' % automake_escape(t)].strip().split()
+ objs = mv["%s_OBJECTS" % automake_escape(t)].strip().split()
+ ldadd = mv["%s_LDADD" % automake_escape(t)].strip().split()
for item in ldadd:
- if item.startswith('-'):
+ if item.startswith("-"):
continue
- if item.endswith('.a'):
- objs.extend(mv['%s_OBJECTS' % automake_escape(item)].strip().split())
+ if item.endswith(".a"):
+ objs.extend(mv["%s_OBJECTS" % automake_escape(item)].strip().split())
- sys.stderr.write('processing %s...\n' % t)
+ sys.stderr.write("processing %s...\n" % t)
sys.stderr.flush()
- #print(t, '\n\t', objs)
+ # print(t, '\n\t', objs)
syms.load(t, objs)
syms.evaluate()
for obj, reports in sorted(syms.report.items()):
- print('%s:' % obj)
+ print("%s:" % obj)
for report in reports:
- print('\t%r' % report)
+ print("\t%r" % report)
write_html_report(syms)