|
Packit |
4afa83 |
#!/usr/bin/env python3
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
import os, sys, glob, pickle, subprocess
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
sys.path.insert(0, os.path.dirname(__file__))
|
|
Packit |
4afa83 |
from clang import cindex
|
|
Packit |
4afa83 |
sys.path = sys.path[1:]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def configure_libclang():
|
|
Packit |
4afa83 |
llvm_libdirs = ['/usr/lib/llvm-3.2/lib', '/usr/lib64/llvm']
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
try:
|
|
Packit |
4afa83 |
libdir = subprocess.check_output(['llvm-config', '--libdir']).decode('utf-8').strip()
|
|
Packit |
4afa83 |
llvm_libdirs.insert(0, libdir)
|
|
Packit |
4afa83 |
except OSError:
|
|
Packit |
4afa83 |
pass
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for d in llvm_libdirs:
|
|
Packit |
4afa83 |
if not os.path.exists(d):
|
|
Packit |
4afa83 |
continue
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
files = glob.glob(os.path.join(d, 'libclang.so*'))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if len(files) != 0:
|
|
Packit |
4afa83 |
cindex.Config.set_library_file(files[0])
|
|
Packit |
4afa83 |
return
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
class Call:
|
|
Packit |
4afa83 |
def __init__(self, cursor, decl):
|
|
Packit |
4afa83 |
self.ident = cursor.displayname.decode('utf-8')
|
|
Packit |
4afa83 |
self.filename = cursor.location.file.name.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
ex = cursor.extent
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.start_line = ex.start.line
|
|
Packit |
4afa83 |
self.start_column = ex.start.column
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.end_line = ex.end.line
|
|
Packit |
4afa83 |
self.end_column = ex.end.column
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.decl_filename = decl.location.file.name.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
class Definition:
|
|
Packit |
4afa83 |
def __init__(self, cursor):
|
|
Packit |
4afa83 |
self.ident = cursor.spelling.decode('utf-8')
|
|
Packit |
4afa83 |
self.display = cursor.displayname.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.filename = cursor.location.file.name.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
ex = cursor.extent
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.start_line = ex.start.line
|
|
Packit |
4afa83 |
self.start_column = ex.start.column
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
self.end_line = ex.end.line
|
|
Packit |
4afa83 |
self.end_column = ex.end.column
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def process_diagnostics(tu):
|
|
Packit |
4afa83 |
diagnostics = tu.diagnostics
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
haserr = False
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for d in diagnostics:
|
|
Packit |
4afa83 |
sys.stderr.write('{0}\n'.format(d.format.decode('utf-8')))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if d.severity > cindex.Diagnostic.Warning:
|
|
Packit |
4afa83 |
haserr = True
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if haserr:
|
|
Packit |
4afa83 |
sys.exit(1)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def walk_cursors(tu, files):
|
|
Packit |
4afa83 |
proc = list(tu.cursor.get_children())
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
while len(proc) > 0:
|
|
Packit |
4afa83 |
cursor = proc[0]
|
|
Packit |
4afa83 |
proc = proc[1:]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if cursor.location.file is None:
|
|
Packit |
4afa83 |
continue
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
fname = cursor.location.file.name.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if fname in files:
|
|
Packit |
4afa83 |
yield cursor
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
proc += list(cursor.get_children())
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def newer(a, b):
|
|
Packit |
4afa83 |
try:
|
|
Packit |
4afa83 |
return os.stat(a).st_mtime > os.stat(b).st_mtime
|
|
Packit |
4afa83 |
except:
|
|
Packit |
4afa83 |
return True
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def scan_libgit2_glib(cflags, files, git2dir):
|
|
Packit |
4afa83 |
files = [os.path.abspath(f) for f in files]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
dname = os.path.dirname(__file__)
|
|
Packit |
4afa83 |
allcalls = {}
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
l = 0
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not os.getenv('SILENT'):
|
|
Packit |
4afa83 |
sys.stderr.write('\n')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
i = 0
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for f in files:
|
|
Packit |
4afa83 |
if not os.getenv('SILENT'):
|
|
Packit |
4afa83 |
name = os.path.basename(f)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if len(name) > l:
|
|
Packit |
4afa83 |
l = len(name)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
perc = int((i / len(files)) * 100)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
sys.stderr.write('[{0: >3}%] Processing ... {1}{2}\r'.format(perc, name, ' ' * (l - len(name))))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
i += 1
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
astf = os.path.join(dname, '.' + os.path.basename(f) + '.cache')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not newer(f, astf):
|
|
Packit |
4afa83 |
with open(astf, 'rb') as fo:
|
|
Packit |
4afa83 |
calls = pickle.load(fo)
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
tu = cindex.TranslationUnit.from_source(f, cflags)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
process_diagnostics(tu)
|
|
Packit |
4afa83 |
calls = {}
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for cursor in walk_cursors(tu, files):
|
|
Packit |
4afa83 |
if cursor.kind == cindex.CursorKind.CALL_EXPR or \
|
|
Packit |
4afa83 |
cursor.kind == cindex.CursorKind.DECL_REF_EXPR:
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
cdecl = cursor.get_referenced()
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if cdecl.kind != cindex.CursorKind.FUNCTION_DECL:
|
|
Packit |
4afa83 |
continue
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if (not cdecl is None) and (not cdecl.location.file is None):
|
|
Packit |
4afa83 |
fdefname = cdecl.location.file.name.decode('utf-8')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if fdefname.startswith(git2dir):
|
|
Packit |
4afa83 |
call = Call(cursor, cdecl)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if call.ident in calls:
|
|
Packit |
4afa83 |
calls[call.ident].append(call)
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
calls[call.ident] = [call]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
with open(astf, 'wb') as fo:
|
|
Packit |
4afa83 |
pickle.dump(calls, fo)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for k in calls:
|
|
Packit |
4afa83 |
if k in allcalls:
|
|
Packit |
4afa83 |
allcalls[k] += calls[k]
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
allcalls[k] = list(calls[k])
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not os.getenv('SILENT'):
|
|
Packit |
4afa83 |
sys.stderr.write('\r[100%] Processing ... done{0}\n'.format(' ' * (l - 4)))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
return allcalls
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
def scan_libgit2(cflags, git2dir):
|
|
Packit |
4afa83 |
tu = cindex.TranslationUnit.from_source(git2dir + '.h', cflags)
|
|
Packit |
4afa83 |
process_diagnostics(tu)
|
|
Packit |
4afa83 |
headers = glob.glob(os.path.join(git2dir, '*.h'))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
defs = {}
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
objapi = ['lookup', 'lookup_prefix', 'free', 'id', 'owner']
|
|
Packit |
4afa83 |
objderiv = ['commit', 'tree', 'tag', 'blob']
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
ignore = set()
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for deriv in objderiv:
|
|
Packit |
4afa83 |
for api in objapi:
|
|
Packit |
4afa83 |
ignore.add('git_' + deriv + '_' + api)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for cursor in walk_cursors(tu, headers):
|
|
Packit |
4afa83 |
if cursor.kind == cindex.CursorKind.FUNCTION_DECL:
|
|
Packit |
4afa83 |
deff = Definition(cursor)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not deff.ident in ignore:
|
|
Packit |
4afa83 |
defs[deff.ident] = deff
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
return defs
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
configure_libclang()
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
pos = sys.argv.index('--')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
cflags = sys.argv[1:pos]
|
|
Packit |
4afa83 |
files = sys.argv[pos+1:]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
incdir = os.getenv('LIBGIT2_INCLUDE_DIR')
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
defs = scan_libgit2(cflags, incdir)
|
|
Packit |
4afa83 |
calls = scan_libgit2_glib(cflags, files, incdir)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
notused = {}
|
|
Packit |
4afa83 |
perfile = {}
|
|
Packit |
4afa83 |
nperfile = {}
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for d in defs:
|
|
Packit |
4afa83 |
o = defs[d]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not d in calls:
|
|
Packit |
4afa83 |
notused[d] = defs[d]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not o.filename in nperfile:
|
|
Packit |
4afa83 |
nperfile[o.filename] = [o]
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
nperfile[o.filename].append(o)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if not o.filename in perfile:
|
|
Packit |
4afa83 |
perfile[o.filename] = [o]
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
perfile[o.filename].append(o)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
ss = [notused[f] for f in notused]
|
|
Packit |
4afa83 |
ss.sort(key=lambda x: '{0} {1}'.format(os.path.basename(x.filename), x.ident))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
lastf = None
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
keys = list(perfile.keys())
|
|
Packit |
4afa83 |
keys.sort()
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for filename in keys:
|
|
Packit |
4afa83 |
b = os.path.basename(filename)
|
|
Packit |
4afa83 |
f = perfile[filename]
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
n_perfile = len(f)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
if filename in nperfile:
|
|
Packit |
4afa83 |
n_nperfile = len(nperfile[filename])
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
n_nperfile = 0
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
perc = int(((n_perfile - n_nperfile) / n_perfile) * 100)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
print('\n File {0}, coverage {1}% ({2} out of {3}):'.format(b, perc, n_perfile - n_nperfile, n_perfile))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
cp = list(f)
|
|
Packit |
4afa83 |
cp.sort(key=lambda x: "{0} {1}".format(not x.ident in calls, x.ident))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
for d in cp:
|
|
Packit |
4afa83 |
if d.ident in calls:
|
|
Packit |
4afa83 |
print(' \033[32m✓ {0}\033[0m'.format(d.display))
|
|
Packit |
4afa83 |
else:
|
|
Packit |
4afa83 |
print(' \033[31m✗ {0}\033[0m'.format(d.display))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
perc = int(((len(defs) - len(notused)) / len(defs)) * 100)
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
print('\nTotal coverage: {0}% ({1} functions out of {2} are being called)\n'.format(perc, len(defs) - len(notused), len(defs)))
|
|
Packit |
4afa83 |
|
|
Packit |
4afa83 |
# vi:ts=4:et
|