[tor-commits] [tor/master] Initial commit of practracker.py .
nickm at torproject.org
nickm at torproject.org
Wed Mar 13 13:30:09 UTC 2019
commit 17dd3167494d69b7afcfdb21395aad135b322722
Author: George Kadianakis <desnacked at riseup.net>
Date: Wed Feb 27 15:14:19 2019 +0200
Initial commit of practracker.py .
---
scripts/maint/practracker/metrics.py | 31 ++++++++
scripts/maint/practracker/practracker.py | 119 +++++++++++++++++++++++++++++++
scripts/maint/practracker/util.py | 23 ++++++
3 files changed, 173 insertions(+)
diff --git a/scripts/maint/practracker/metrics.py b/scripts/maint/practracker/metrics.py
new file mode 100644
index 000000000..43ec3f809
--- /dev/null
+++ b/scripts/maint/practracker/metrics.py
@@ -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)
diff --git a/scripts/maint/practracker/practracker.py b/scripts/maint/practracker/practracker.py
new file mode 100755
index 000000000..c95c432c3
--- /dev/null
+++ b/scripts/maint/practracker/practracker.py
@@ -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()
diff --git a/scripts/maint/practracker/util.py b/scripts/maint/practracker/util.py
new file mode 100644
index 000000000..8e9d95ece
--- /dev/null
+++ b/scripts/maint/practracker/util.py
@@ -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
+
More information about the tor-commits
mailing list