Loading changes/ticket31634 0 → 100644 +4 −0 Original line number Diff line number Diff line o Minor features (testing, architeture): - Our test scripts now double-check that subsystem initialization order is consistent with the inter-module dependencies established by our .may_include files. Implements ticket 31634. doc/tor.1.txt +7 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,13 @@ The following options in this section are only recognized on the ISO-8601 format. For example, the output sent to stdout will be of the form: "signing-cert-expiry: 2017-07-25 08:30:15 UTC" [[opt-dbg]] **--dbg-**...:: Tor may support other options beginning with the string "dbg". These are intended for use by developers to debug and test Tor. They are not supported or guaranteed to be stable, and you should probably not use them. [[conf-format]] == THE CONFIGURATION FILE FORMAT Loading scripts/maint/practracker/includes.py +88 −2 Original line number Diff line number Diff line Loading @@ -59,6 +59,8 @@ ALLOWED_PATTERNS = [ re.compile(r'^micro-revision.i$'), ] TOPDIR = "src" def pattern_is_normal(s): for p in ALLOWED_PATTERNS: if p.match(s): Loading Loading @@ -136,6 +138,29 @@ class Rules(object): return allowed def normalize_srcdir(fname): """given the name of a source directory or file, return its name relative to `src` in a unix-like format. """ orig = fname dirname, dirfile = os.path.split(fname) if re.match(r'.*\.[ch]$', dirfile): fname = dirname # Now we have a directory. dirname, result = os.path.split(fname) for _ in range(100): # prevent excess looping in case I missed a tricky case dirname, dirpart = os.path.split(dirname) if dirpart == 'src' or dirname == "": #print(orig,"=>",result) return result result = "{}/{}".format(dirpart,result) print("No progress!") assert False include_rules_cache = {} def load_include_rules(fname): Loading Loading @@ -173,6 +198,27 @@ def remove_self_edges(graph): for k in list(graph): graph[k] = [ d for d in graph[k] if d != k ] def closure(graph): """Takes a directed graph in as an adjacency mapping (a mapping from node to a list of the nodes to which it connects), and completes its closure. """ graph = graph.copy() changed = False for k in graph.keys(): graph[k] = set(graph[k]) while True: for k in graph.keys(): sz = len(graph[k]) for v in list(graph[k]): graph[k].update(graph.get(v, [])) if sz != len(graph[k]): changed = True if not changed: return graph changed = False def toposort(graph, limit=100): """Takes a directed graph in as an adjacency mapping (a mapping from node to a list of the nodes to which it connects). Tries to Loading Loading @@ -233,8 +279,38 @@ def walk_c_files(topdir="src"): for err in consider_include_rules(fullpath, f): yield err def open_or_stdin(fname): if fname == '-': return sys.stdin else: return open(fname) def check_subsys_file(fname, uses_dirs): if not uses_dirs: # We're doing a distcheck build, or for some other reason there are # no .may_include files. print("SKIPPING") return False uses_dirs = { normalize_srcdir(k) : { normalize_srcdir(d) for d in v } for (k,v) in uses_dirs.items() } uses_closure = closure(uses_dirs) ok = True previous_subsystems = [] with open_or_stdin(fname) as f: for line in f: _, name, fname = line.split() fname = normalize_srcdir(fname) for prev in previous_subsystems: if fname in uses_closure[prev]: print("INVERSION: {} uses {}".format(prev,fname)) ok = False previous_subsystems.append(fname) return not ok def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, list_advisories=False): list_advisories=False, check_subsystem_order=None): trouble = False for err in walk_c_files(topdir): Loading @@ -259,6 +335,11 @@ def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, uses_dirs[rules.incpath] = rules.getAllowedDirectories() remove_self_edges(uses_dirs) if check_subsystem_order: if check_subsys_file(check_subsystem_order, uses_dirs): sys.exit(1) all_levels = toposort(uses_dirs) if log_sorted_levels: Loading @@ -282,14 +363,19 @@ def main(argv): help="List unused lines in .may_include files.") parser.add_argument("--list-advisories", action="store_true", help="List advisories as well as forbidden includes") parser.add_argument("--check-subsystem-order", action="store", help="Check a list of subsystems for ordering") parser.add_argument("topdir", default="src", nargs="?", help="Top-level directory for the tor source") args = parser.parse_args(argv[1:]) global TOPDIR TOPDIR = args.topdir run_check_includes(topdir=args.topdir, log_sorted_levels=args.toposort, list_unused=args.list_unused, list_advisories=args.list_advisories) list_advisories=args.list_advisories, check_subsystem_order=args.check_subsystem_order) if __name__ == '__main__': main(sys.argv) scripts/maint/run_check_subsystem_order.sh 0 → 100755 +17 −0 Original line number Diff line number Diff line #!/usr/bin/env bash set -e TOR="${abs_top_builddir:-.}/src/app/tor" INCLUDES_PY="${abs_top_srcdir:-.}/scripts/maint/practracker/includes.py" if ! test -x "${INCLUDES_PY}" ; then echo "skip" exit 77 fi "${TOR}" --dbg-dump-subsystem-list | \ "${INCLUDES_PY}" --check-subsystem-order - "${abs_top_srcdir}/src" echo ok src/app/config/config.c +7 −0 Original line number Diff line number Diff line Loading @@ -2498,6 +2498,9 @@ static const struct { .command=CMD_IMMEDIATE }, { .name="--nt-service" }, { .name="-nt-service" }, { .name="--dbg-dump-subsystem-list", .command=CMD_IMMEDIATE, .quiet=QUIET_HUSH }, { .name=NULL }, }; Loading Loading @@ -4604,6 +4607,10 @@ options_init_from_torrc(int argc, char **argv) list_deprecated_options(); return 1; } if (config_line_find(cmdline_only_options, "--dbg-dump-subsystem-list")) { subsystems_dump_list(); return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); Loading Loading
changes/ticket31634 0 → 100644 +4 −0 Original line number Diff line number Diff line o Minor features (testing, architeture): - Our test scripts now double-check that subsystem initialization order is consistent with the inter-module dependencies established by our .may_include files. Implements ticket 31634.
doc/tor.1.txt +7 −0 Original line number Diff line number Diff line Loading @@ -186,6 +186,13 @@ The following options in this section are only recognized on the ISO-8601 format. For example, the output sent to stdout will be of the form: "signing-cert-expiry: 2017-07-25 08:30:15 UTC" [[opt-dbg]] **--dbg-**...:: Tor may support other options beginning with the string "dbg". These are intended for use by developers to debug and test Tor. They are not supported or guaranteed to be stable, and you should probably not use them. [[conf-format]] == THE CONFIGURATION FILE FORMAT Loading
scripts/maint/practracker/includes.py +88 −2 Original line number Diff line number Diff line Loading @@ -59,6 +59,8 @@ ALLOWED_PATTERNS = [ re.compile(r'^micro-revision.i$'), ] TOPDIR = "src" def pattern_is_normal(s): for p in ALLOWED_PATTERNS: if p.match(s): Loading Loading @@ -136,6 +138,29 @@ class Rules(object): return allowed def normalize_srcdir(fname): """given the name of a source directory or file, return its name relative to `src` in a unix-like format. """ orig = fname dirname, dirfile = os.path.split(fname) if re.match(r'.*\.[ch]$', dirfile): fname = dirname # Now we have a directory. dirname, result = os.path.split(fname) for _ in range(100): # prevent excess looping in case I missed a tricky case dirname, dirpart = os.path.split(dirname) if dirpart == 'src' or dirname == "": #print(orig,"=>",result) return result result = "{}/{}".format(dirpart,result) print("No progress!") assert False include_rules_cache = {} def load_include_rules(fname): Loading Loading @@ -173,6 +198,27 @@ def remove_self_edges(graph): for k in list(graph): graph[k] = [ d for d in graph[k] if d != k ] def closure(graph): """Takes a directed graph in as an adjacency mapping (a mapping from node to a list of the nodes to which it connects), and completes its closure. """ graph = graph.copy() changed = False for k in graph.keys(): graph[k] = set(graph[k]) while True: for k in graph.keys(): sz = len(graph[k]) for v in list(graph[k]): graph[k].update(graph.get(v, [])) if sz != len(graph[k]): changed = True if not changed: return graph changed = False def toposort(graph, limit=100): """Takes a directed graph in as an adjacency mapping (a mapping from node to a list of the nodes to which it connects). Tries to Loading Loading @@ -233,8 +279,38 @@ def walk_c_files(topdir="src"): for err in consider_include_rules(fullpath, f): yield err def open_or_stdin(fname): if fname == '-': return sys.stdin else: return open(fname) def check_subsys_file(fname, uses_dirs): if not uses_dirs: # We're doing a distcheck build, or for some other reason there are # no .may_include files. print("SKIPPING") return False uses_dirs = { normalize_srcdir(k) : { normalize_srcdir(d) for d in v } for (k,v) in uses_dirs.items() } uses_closure = closure(uses_dirs) ok = True previous_subsystems = [] with open_or_stdin(fname) as f: for line in f: _, name, fname = line.split() fname = normalize_srcdir(fname) for prev in previous_subsystems: if fname in uses_closure[prev]: print("INVERSION: {} uses {}".format(prev,fname)) ok = False previous_subsystems.append(fname) return not ok def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, list_advisories=False): list_advisories=False, check_subsystem_order=None): trouble = False for err in walk_c_files(topdir): Loading @@ -259,6 +335,11 @@ def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, uses_dirs[rules.incpath] = rules.getAllowedDirectories() remove_self_edges(uses_dirs) if check_subsystem_order: if check_subsys_file(check_subsystem_order, uses_dirs): sys.exit(1) all_levels = toposort(uses_dirs) if log_sorted_levels: Loading @@ -282,14 +363,19 @@ def main(argv): help="List unused lines in .may_include files.") parser.add_argument("--list-advisories", action="store_true", help="List advisories as well as forbidden includes") parser.add_argument("--check-subsystem-order", action="store", help="Check a list of subsystems for ordering") parser.add_argument("topdir", default="src", nargs="?", help="Top-level directory for the tor source") args = parser.parse_args(argv[1:]) global TOPDIR TOPDIR = args.topdir run_check_includes(topdir=args.topdir, log_sorted_levels=args.toposort, list_unused=args.list_unused, list_advisories=args.list_advisories) list_advisories=args.list_advisories, check_subsystem_order=args.check_subsystem_order) if __name__ == '__main__': main(sys.argv)
scripts/maint/run_check_subsystem_order.sh 0 → 100755 +17 −0 Original line number Diff line number Diff line #!/usr/bin/env bash set -e TOR="${abs_top_builddir:-.}/src/app/tor" INCLUDES_PY="${abs_top_srcdir:-.}/scripts/maint/practracker/includes.py" if ! test -x "${INCLUDES_PY}" ; then echo "skip" exit 77 fi "${TOR}" --dbg-dump-subsystem-list | \ "${INCLUDES_PY}" --check-subsystem-order - "${abs_top_srcdir}/src" echo ok
src/app/config/config.c +7 −0 Original line number Diff line number Diff line Loading @@ -2498,6 +2498,9 @@ static const struct { .command=CMD_IMMEDIATE }, { .name="--nt-service" }, { .name="-nt-service" }, { .name="--dbg-dump-subsystem-list", .command=CMD_IMMEDIATE, .quiet=QUIET_HUSH }, { .name=NULL }, }; Loading Loading @@ -4604,6 +4607,10 @@ options_init_from_torrc(int argc, char **argv) list_deprecated_options(); return 1; } if (config_line_find(cmdline_only_options, "--dbg-dump-subsystem-list")) { subsystems_dump_list(); return 1; } if (config_line_find(cmdline_only_options, "--version")) { printf("Tor version %s.\n",get_version()); Loading