[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