Initial commit of practracker.py .

This commit is contained in:
George Kadianakis 2019-02-27 15:14:19 +02:00
parent 39a1049932
commit 17dd316749
3 changed files with 173 additions and 0 deletions

View File

@ -0,0 +1,31 @@
#!/usr/bin/python
import re
def file_len(f):
"""Get file length of file"""
for i, l in enumerate(f):
pass
return i + 1
def function_lines(f):
"""
Return iterator which iterates over functions and returns (function name, function lines)
"""
# XXX Buggy! Doesn't work with MOCK_IMPL and ENABLE_GCC_WARNINGS
in_function = False
for lineno, line in enumerate(f):
if not in_function:
# find the start of a function
m = re.match(r'^([a-zA-Z_][a-zA-Z_0-9]*),?\(', line)
if m:
func_name = m.group(1)
func_start = lineno
in_function = True
else:
# Fund the end of a function
if line.startswith("}"):
n_lines = lineno - func_start
in_function = False
yield (func_name, n_lines)

View File

@ -0,0 +1,119 @@
#!/usr/bin/python
"""
Tor code best-practices tracker
Go through the various .c files and collect metrics about them. If the metrics
violate some of our best practices and they are not found in the optional
exceptions file ("./exceptions.txt"), then log a violation about them.
The exceptions file is meant to be initialized with the current state of the
source code as follows: ./practracker.py > ./exceptions.txt
We currently do metrics about file size, function size and number of includes.
TODO:
- How is this tool supposed to be used? How should the exception file work?
How should the UI work? Does it need special exit codes?
- Fix the function_length function so that practracker_tests.py passes.
"""
import os, sys
import metrics
import util
# We don't want to run metrics for unittests, automatically-generated C files,
# external libraries or git leftovers.
EXCLUDE_SOURCE_DIRS = ["/src/test/", "/src/trunnel/", "/src/ext/", "/.git/"]
# Where the Tor source code is
TOR_TOPDIR = "../../../"
# An optional exceptions_file
EXCEPTIONS_FILE = "./exceptions.txt"
# Recommended file size
MAX_FILE_SIZE = 3000 # lines
# Recommended function size
MAX_FUNCTION_SIZE = 100 # lines
# Recommended number of #includes
MAX_INCLUDE_COUNT = 50
#######################################################
def print_violation_if_not_exception(violation_str, exceptions_str):
# Check if this violation is already in the optional exceptions file
if exceptions_str and violation_str in exceptions_str:
return
print violation_str
#######################################################
def consider_file_size(fname, f, exceptions_str):
file_size = metrics.file_len(f)
if file_size > MAX_FILE_SIZE:
violation_str = "violation file-size %s %d" % (fname, file_size)
print_violation_if_not_exception(violation_str, exceptions_str)
def consider_includes(fname, f, exceptions_str):
include_count = 0
for _, line in enumerate(f):
if line.startswith("#include "):
include_count += 1
if include_count > MAX_INCLUDE_COUNT:
violation_str = "violation include-count %s %d" % (fname, include_count)
print_violation_if_not_exception(violation_str, exceptions_str)
def consider_function_size(fname, f, exceptions_str):
for name, lines in metrics.function_lines(f):
# Don't worry about functions within our limits
if lines <= MAX_FUNCTION_SIZE:
continue
# That's a big function! Issue a violation!
canonical_function_name = "%s:%s()" % (fname,name)
violation_str = "violation function-size %s %s" % (lines, canonical_function_name)
print_violation_if_not_exception(violation_str, exceptions_str)
#######################################################
def consider_all_metrics(files_list, exceptions_str):
"""Consider metrics for all files"""
for fname in files_list:
with open(fname, 'r') as f:
consider_metrics_for_file(fname, f, exceptions_str)
def consider_metrics_for_file(fname, f, exceptions_str):
"""
Get metrics for file with filename 'fname' and file descriptor 'f'.
"""
# Get file length
consider_file_size(fname, f, exceptions_str)
# Consider number of #includes
f.seek(0)
consider_includes(fname, f, exceptions_str)
# Get function length
f.seek(0)
consider_function_size(fname, f, exceptions_str)
def main():
# 1) Get all the .c files we care about
files_list = util.get_tor_c_files(TOR_TOPDIR, EXCLUDE_SOURCE_DIRS)
# 2) Read an optional exceptions file so that we don't warn about the past
exceptions_str = None
try:
with open(EXCEPTIONS_FILE, 'r') as exception_f:
exceptions_str = exception_f.read()
except IOError:
print "No exception file provided"
# 3) Go through all the files and report violations if they are not exceptions
consider_all_metrics(files_list, exceptions_str)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,23 @@
import os
def get_tor_c_files(tor_topdir, exclude_dirs):
"""
Return a list with the .c filenames we want to get metrics of.
"""
files_list = []
for root, directories, filenames in os.walk(tor_topdir):
for filename in filenames:
# We only care about .c files
if not filename.endswith(".c"):
continue
# Exclude the excluded paths
full_path = os.path.join(root,filename)
if any(exclude_dir in full_path for exclude_dir in exclude_dirs):
continue
files_list.append(full_path)
return files_list