summaryrefslogtreecommitdiff
path: root/scripts/maint
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/maint')
-rwxr-xr-xscripts/maint/add_c_file.py251
-rwxr-xr-xscripts/maint/checkIncludes.py2
-rwxr-xr-xscripts/maint/checkSpace.pl32
-rw-r--r--scripts/maint/practracker/exceptions.txt289
-rw-r--r--scripts/maint/practracker/metrics.py50
-rwxr-xr-xscripts/maint/practracker/practracker.py216
-rwxr-xr-xscripts/maint/practracker/practracker_tests.py50
-rw-r--r--scripts/maint/practracker/problem.py158
-rw-r--r--scripts/maint/practracker/util.py28
-rwxr-xr-xscripts/maint/pre-commit.git-hook26
-rwxr-xr-xscripts/maint/pre-push.git-hook61
-rwxr-xr-xscripts/maint/rectify_include_paths.py15
-rwxr-xr-xscripts/maint/updateCopyright.pl6
13 files changed, 1091 insertions, 93 deletions
diff --git a/scripts/maint/add_c_file.py b/scripts/maint/add_c_file.py
new file mode 100755
index 0000000000..499415974f
--- /dev/null
+++ b/scripts/maint/add_c_file.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python3
+
+"""
+ Add a C file with matching header to the Tor codebase. Creates
+ both files from templates, and adds them to the right include.am file.
+
+ Example usage:
+
+ % add_c_file.py ./src/feature/dirauth/ocelot.c
+"""
+
+import os
+import re
+import time
+
+def topdir_file(name):
+ """Strip opening "src" from a filename"""
+ if name.startswith("src/"):
+ name = name[4:]
+ return name
+
+def guard_macro(name):
+ """Return the guard macro that should be used for the header file 'name'.
+ """
+ td = topdir_file(name).replace(".", "_").replace("/", "_").upper()
+ return "TOR_{}".format(td)
+
+def makeext(name, new_extension):
+ """Replace the extension for the file called 'name' with 'new_extension'.
+ """
+ base = os.path.splitext(name)[0]
+ return base + "." + new_extension
+
+def instantiate_template(template, output_fname):
+ """
+ Fill in a template with string using the fields that should be used
+ for 'output_fname'.
+ """
+ names = {
+ # The relative location of the header file.
+ 'header_path' : makeext(topdir_file(output_fname), "h"),
+ # The relative location of the C file file.
+ 'c_file_path' : makeext(topdir_file(output_fname), "c"),
+ # The truncated name of the file.
+ 'short_name' : os.path.basename(output_fname),
+ # The current year, for the copyright notice
+ 'this_year' : time.localtime().tm_year,
+ # An appropriate guard macro, for the header.
+ 'guard_macro' : guard_macro(output_fname),
+ }
+
+ return template.format(**names)
+
+HEADER_TEMPLATE = """\
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-{this_year}, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file {short_name}
+ * @brief Header for {c_file_path}
+ **/
+
+#ifndef {guard_macro}
+#define {guard_macro}
+
+#endif /* !defined({guard_macro}) */
+"""
+
+C_FILE_TEMPLATE = """\
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-{this_year}, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * @file {short_name}
+ * @brief DOCDOC
+ **/
+
+#include "orconfig.h"
+#include "{header_path}"
+"""
+
+class AutomakeChunk:
+ """
+ Represents part of an automake file. If it is decorated with
+ an ADD_C_FILE comment, it has a "kind" based on what to add to it.
+ Otherwise, it only has a bunch of lines in it.
+ """
+ pat = re.compile(r'# ADD_C_FILE: INSERT (\S*) HERE', re.I)
+
+ def __init__(self):
+ self.lines = []
+ self.kind = ""
+
+ def addLine(self, line):
+ """
+ Insert a line into this chunk while parsing the automake file.
+ """
+ m = self.pat.match(line)
+ if m:
+ if self.lines:
+ raise ValueError("control line not preceded by a blank line")
+ self.kind = m.group(1)
+
+ self.lines.append(line)
+ if line.strip() == "":
+ return True
+
+ return False
+
+ def insertMember(self, member):
+ """
+ Add a new member to this chunk. Try to insert it in alphabetical
+ order with matching indentation, but don't freak out too much if the
+ source isn't consistent.
+
+ Assumes that this chunk is of the form:
+ FOOBAR = \
+ X \
+ Y \
+ Z
+ """
+ self.prespace = "\t"
+ self.postspace = "\t\t"
+ for lineno, line in enumerate(self.lines):
+ m = re.match(r'(\s+)(\S+)(\s+)\\', line)
+ if not m:
+ continue
+ prespace, fname, postspace = m.groups()
+ if fname > member:
+ self.insert_before(lineno, member, prespace, postspace)
+ return
+ self.insert_at_end(member)
+
+ def insert_before(self, lineno, member, prespace, postspace):
+ self.lines.insert(lineno,
+ "{}{}{}\\\n".format(prespace, member, postspace))
+
+ def insert_at_end(self, member, prespace, postspace):
+ lastline = self.lines[-1]
+ self.lines[-1] += '{}\\\n'.format(postspace)
+ self.lines.append("{}{}\n".format(prespace, member))
+
+ def dump(self, f):
+ """Write all the lines in this chunk to the file 'f'."""
+ for line in self.lines:
+ f.write(line)
+ if not line.endswith("\n"):
+ f.write("\n")
+
+class ParsedAutomake:
+ """A sort-of-parsed automake file, with identified chunks into which
+ headers and c files can be inserted.
+ """
+ def __init__(self):
+ self.chunks = []
+ self.by_type = {}
+
+ def addChunk(self, chunk):
+ """Add a newly parsed AutomakeChunk to this file."""
+ self.chunks.append(chunk)
+ self.by_type[chunk.kind.lower()] = chunk
+
+ def add_file(self, fname, kind):
+ """Insert a file of kind 'kind' to the appropriate section of this
+ file. Return True if we added it.
+ """
+ if kind.lower() in self.by_type:
+ self.by_type[kind.lower()].insertMember(fname)
+ return True
+ else:
+ return False
+
+ def dump(self, f):
+ """Write this file into a file 'f'."""
+ for chunk in self.chunks:
+ chunk.dump(f)
+
+def get_include_am_location(fname):
+ """Find the right include.am file for introducing a new file. Return None
+ if we can't guess one.
+
+ Note that this function is imperfect because our include.am layout is
+ not (yet) consistent.
+ """
+ td = topdir_file(fname)
+ m = re.match(r'^lib/([a-z0-9_]*)/', td)
+ if m:
+ return "src/lib/{}/include.am".format(m.group(1))
+
+ if re.match(r'^(core|feature|app)/', td):
+ return "src/core/include.am"
+
+ if re.match(r'^test/', td):
+ return "src/test/include.am"
+
+ return None
+
+def run(fn):
+ """
+ Create a new C file and H file corresponding to the filename "fn", and
+ add them to include.am.
+ """
+
+ cf = makeext(fn, "c")
+ hf = makeext(fn, "h")
+
+ if os.path.exists(cf):
+ print("{} already exists".format(cf))
+ return 1
+ if os.path.exists(hf):
+ print("{} already exists".format(hf))
+ return 1
+
+ with open(cf, 'w') as f:
+ f.write(instantiate_template(C_FILE_TEMPLATE, cf))
+
+ with open(hf, 'w') as f:
+ f.write(instantiate_template(HEADER_TEMPLATE, hf))
+
+ iam = get_include_am_location(cf)
+ if iam is None or not os.path.exists(iam):
+ print("Made files successfully but couldn't identify include.am for {}"
+ .format(cf))
+ return 1
+
+ amfile = ParsedAutomake()
+ cur_chunk = AutomakeChunk()
+ with open(iam) as f:
+ for line in f:
+ if cur_chunk.addLine(line):
+ amfile.addChunk(cur_chunk)
+ cur_chunk = AutomakeChunk()
+ amfile.addChunk(cur_chunk)
+
+ amfile.add_file(cf, "sources")
+ amfile.add_file(hf, "headers")
+
+ with open(iam+".tmp", 'w') as f:
+ amfile.dump(f)
+
+ os.rename(iam+".tmp", iam)
+
+if __name__ == '__main__':
+ import sys
+ sys.exit(run(sys.argv[1]))
diff --git a/scripts/maint/checkIncludes.py b/scripts/maint/checkIncludes.py
index 3afd9bbebe..ec9350b9b1 100755
--- a/scripts/maint/checkIncludes.py
+++ b/scripts/maint/checkIncludes.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/python
# Copyright 2018 The Tor Project, Inc. See LICENSE file for licensing info.
"""This script looks through all the directories for files matching *.c or
diff --git a/scripts/maint/checkSpace.pl b/scripts/maint/checkSpace.pl
index 633b47e314..433ae62807 100755
--- a/scripts/maint/checkSpace.pl
+++ b/scripts/maint/checkSpace.pl
@@ -18,6 +18,8 @@ if ($ARGV[0] =~ /^-/) {
our %basenames = ();
+our %guardnames = ();
+
for my $fn (@ARGV) {
open(F, "$fn");
my $lastnil = 0;
@@ -31,6 +33,10 @@ for my $fn (@ARGV) {
} else {
$basenames{$basename} = $fn;
}
+ my $isheader = ($fn =~ /\.h/);
+ my $seenguard = 0;
+ my $guardname = "<none>";
+
while (<F>) {
## Warn about windows-style newlines.
# (We insist on lines that end with a single LF character, not
@@ -112,6 +118,23 @@ for my $fn (@ARGV) {
next;
}
}
+
+ if ($isheader) {
+ if ($seenguard == 0) {
+ if (/ifndef\s+(\S+)/) {
+ ++$seenguard;
+ $guardname = $1;
+ }
+ } elsif ($seenguard == 1) {
+ if (/^\#define (\S+)/) {
+ ++$seenguard;
+ if ($1 ne $guardname) {
+ msg "GUARD:$fn:$.: Header guard macro mismatch.\n";
+ }
+ }
+ }
+ }
+
if (m!/\*.*?\*/!) {
s!\s*/\*.*?\*/!!;
} elsif (m!/\*!) {
@@ -201,6 +224,15 @@ for my $fn (@ARGV) {
}
}
}
+ if ($isheader && $C) {
+ if ($seenguard < 2) {
+ msg "$fn:No #ifndef/#define header guard pair found.\n";
+ } elsif ($guardnames{$guardname}) {
+ msg "$fn:Guard macro $guardname also used in $guardnames{$guardname}\n";
+ } else {
+ $guardnames{$guardname} = $fn;
+ }
+ }
close(F);
}
diff --git a/scripts/maint/practracker/exceptions.txt b/scripts/maint/practracker/exceptions.txt
new file mode 100644
index 0000000000..726dc9c3ef
--- /dev/null
+++ b/scripts/maint/practracker/exceptions.txt
@@ -0,0 +1,289 @@
+# Welcome to the exceptions file for Tor's best-practices tracker!
+#
+# Each line of this file represents a single violation of Tor's best
+# practices -- typically, a violation that we had before practracker.py
+# first existed.
+#
+# There are three kinds of problems that we recognize right now:
+# function-size -- a function of more than 100 lines.
+# file-size -- a file of more than 3000 lines.
+# include-count -- a file with more than 50 #includes.
+#
+# Each line below represents a single exception that practracker should
+# _ignore_. Each line has four parts:
+# 1. The word "problem".
+# 2. The kind of problem.
+# 3. The location of the problem: either a filename, or a
+# filename:functionname pair.
+# 4. The magnitude of the problem to ignore.
+#
+# So for example, consider this line:
+# problem file-size /src/core/or/connection_or.c 3200
+#
+# It tells practracker to allow the mentioned file to be up to 3200 lines
+# long, even though ordinarily it would warn about any file with more than
+# 3000 lines.
+#
+# You can either edit this file by hand, or regenerate it completely by
+# running `make practracker-regen`.
+#
+# Remember: It is better to fix the problem than to add a new exception!
+
+problem file-size /src/app/config/config.c 8518
+problem include-count /src/app/config/config.c 87
+problem function-size /src/app/config/config.c:options_act_reversible() 296
+problem function-size /src/app/config/config.c:options_act() 589
+problem function-size /src/app/config/config.c:resolve_my_address() 192
+problem function-size /src/app/config/config.c:options_validate() 1217
+problem function-size /src/app/config/config.c:options_init_from_torrc() 207
+problem function-size /src/app/config/config.c:options_init_from_string() 173
+problem function-size /src/app/config/config.c:options_init_logs() 146
+problem function-size /src/app/config/config.c:parse_bridge_line() 104
+problem function-size /src/app/config/config.c:parse_transport_line() 191
+problem function-size /src/app/config/config.c:parse_dir_authority_line() 151
+problem function-size /src/app/config/config.c:parse_dir_fallback_line() 102
+problem function-size /src/app/config/config.c:parse_port_config() 452
+problem function-size /src/app/config/config.c:parse_ports() 170
+problem function-size /src/app/config/config.c:getinfo_helper_config() 116
+problem function-size /src/app/config/confparse.c:config_assign_value() 205
+problem function-size /src/app/config/confparse.c:config_get_assigned_option() 129
+problem include-count /src/app/main/main.c 67
+problem function-size /src/app/main/main.c:dumpstats() 102
+problem function-size /src/app/main/main.c:tor_init() 137
+problem function-size /src/app/main/main.c:sandbox_init_filter() 291
+problem function-size /src/app/main/main.c:run_tor_main_loop() 105
+problem function-size /src/app/main/ntmain.c:nt_service_install() 125
+problem file-size /src/core/mainloop/connection.c 5569
+problem include-count /src/core/mainloop/connection.c 62
+problem function-size /src/core/mainloop/connection.c:connection_free_minimal() 185
+problem function-size /src/core/mainloop/connection.c:connection_listener_new() 328
+problem function-size /src/core/mainloop/connection.c:connection_handle_listener_read() 161
+problem function-size /src/core/mainloop/connection.c:connection_connect_sockaddr() 103
+problem function-size /src/core/mainloop/connection.c:connection_proxy_connect() 148
+problem function-size /src/core/mainloop/connection.c:connection_read_proxy_handshake() 153
+problem function-size /src/core/mainloop/connection.c:retry_listener_ports() 116
+problem function-size /src/core/mainloop/connection.c:connection_handle_read_impl() 111
+problem function-size /src/core/mainloop/connection.c:connection_buf_read_from_socket() 181
+problem function-size /src/core/mainloop/connection.c:connection_handle_write_impl() 241
+problem function-size /src/core/mainloop/connection.c:assert_connection_ok() 143
+problem include-count /src/core/mainloop/mainloop.c 63
+problem function-size /src/core/mainloop/mainloop.c:conn_close_if_marked() 108
+problem function-size /src/core/mainloop/mainloop.c:run_connection_housekeeping() 123
+problem file-size /src/core/or/channel.c 3487
+problem function-size /src/core/or/channeltls.c:channel_tls_handle_var_cell() 160
+problem function-size /src/core/or/channeltls.c:channel_tls_process_versions_cell() 170
+problem function-size /src/core/or/channeltls.c:channel_tls_process_netinfo_cell() 214
+problem function-size /src/core/or/channeltls.c:channel_tls_process_certs_cell() 246
+problem function-size /src/core/or/channeltls.c:channel_tls_process_authenticate_cell() 202
+problem include-count /src/core/or/circuitbuild.c 54
+problem function-size /src/core/or/circuitbuild.c:get_unique_circ_id_by_chan() 128
+problem function-size /src/core/or/circuitbuild.c:circuit_extend() 147
+problem function-size /src/core/or/circuitbuild.c:choose_good_exit_server_general() 206
+problem include-count /src/core/or/circuitlist.c 55
+problem function-size /src/core/or/circuitlist.c:HT_PROTOTYPE() 128
+problem function-size /src/core/or/circuitlist.c:circuit_free_() 143
+problem function-size /src/core/or/circuitlist.c:circuit_find_to_cannibalize() 102
+problem function-size /src/core/or/circuitlist.c:circuit_about_to_free() 120
+problem function-size /src/core/or/circuitlist.c:circuits_handle_oom() 117
+problem function-size /src/core/or/circuitmux.c:circuitmux_set_policy() 110
+problem function-size /src/core/or/circuitmux.c:circuitmux_attach_circuit() 114
+problem file-size /src/core/or/circuitpadding.c 3040
+problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 107
+problem function-size /src/core/or/circuitpadding.c:circpad_machine_schedule_padding() 113
+problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_relay_hide_intro_circuits() 104
+problem function-size /src/core/or/circuitpadding_machines.c:circpad_machine_client_hide_rend_circuits() 112
+problem function-size /src/core/or/circuitstats.c:circuit_build_times_parse_state() 124
+problem file-size /src/core/or/circuituse.c 3162
+problem function-size /src/core/or/circuituse.c:circuit_is_acceptable() 132
+problem function-size /src/core/or/circuituse.c:circuit_expire_building() 394
+problem function-size /src/core/or/circuituse.c:circuit_log_ancient_one_hop_circuits() 126
+problem function-size /src/core/or/circuituse.c:circuit_build_failed() 149
+problem function-size /src/core/or/circuituse.c:circuit_launch_by_extend_info() 110
+problem function-size /src/core/or/circuituse.c:circuit_get_open_circ_or_launch() 354
+problem function-size /src/core/or/circuituse.c:connection_ap_handshake_attach_circuit() 244
+problem function-size /src/core/or/command.c:command_process_create_cell() 156
+problem function-size /src/core/or/command.c:command_process_relay_cell() 132
+problem file-size /src/core/or/connection_edge.c 4595
+problem include-count /src/core/or/connection_edge.c 65
+problem function-size /src/core/or/connection_edge.c:connection_ap_expire_beginning() 117
+problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite() 192
+problem function-size /src/core/or/connection_edge.c:connection_ap_handle_onion() 188
+problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_rewrite_and_attach() 423
+problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_send_begin() 111
+problem function-size /src/core/or/connection_edge.c:connection_ap_handshake_socks_resolved() 106
+problem function-size /src/core/or/connection_edge.c:connection_exit_begin_conn() 184
+problem function-size /src/core/or/connection_edge.c:connection_exit_connect() 102
+problem file-size /src/core/or/connection_or.c 3124
+problem include-count /src/core/or/connection_or.c 51
+problem function-size /src/core/or/connection_or.c:connection_or_group_set_badness_() 105
+problem function-size /src/core/or/connection_or.c:connection_or_client_learned_peer_id() 144
+problem function-size /src/core/or/connection_or.c:connection_or_compute_authenticate_cell_body() 235
+problem file-size /src/core/or/policies.c 3249
+problem function-size /src/core/or/policies.c:policy_summarize() 107
+problem function-size /src/core/or/protover.c:protover_all_supported() 117
+problem file-size /src/core/or/relay.c 3244
+problem function-size /src/core/or/relay.c:circuit_receive_relay_cell() 127
+problem function-size /src/core/or/relay.c:relay_send_command_from_edge_() 112
+problem function-size /src/core/or/relay.c:connection_ap_process_end_not_open() 194
+problem function-size /src/core/or/relay.c:connection_edge_process_relay_cell_not_open() 139
+problem function-size /src/core/or/relay.c:connection_edge_process_relay_cell() 430
+problem function-size /src/core/or/relay.c:connection_edge_package_raw_inbuf() 129
+problem function-size /src/core/or/relay.c:circuit_resume_edge_reading_helper() 148
+problem function-size /src/core/or/scheduler_kist.c:kist_scheduler_run() 171
+problem function-size /src/core/or/scheduler_vanilla.c:vanilla_scheduler_run() 109
+problem function-size /src/core/or/versions.c:tor_version_parse() 104
+problem function-size /src/core/proto/proto_socks.c:parse_socks_client() 112
+problem function-size /src/feature/client/addressmap.c:addressmap_rewrite() 112
+problem function-size /src/feature/client/bridges.c:rewrite_node_address_for_bridge() 126
+problem function-size /src/feature/client/circpathbias.c:pathbias_measure_close_rate() 108
+problem function-size /src/feature/client/dnsserv.c:evdns_server_callback() 153
+problem file-size /src/feature/client/entrynodes.c 3824
+problem function-size /src/feature/client/entrynodes.c:entry_guards_upgrade_waiting_circuits() 157
+problem function-size /src/feature/client/entrynodes.c:entry_guard_parse_from_state() 246
+problem function-size /src/feature/client/transports.c:handle_proxy_line() 108
+problem function-size /src/feature/client/transports.c:parse_method_line_helper() 112
+problem function-size /src/feature/client/transports.c:create_managed_proxy_environment() 109
+problem function-size /src/feature/control/control.c:connection_control_process_inbuf() 136
+problem function-size /src/feature/control/control_auth.c:handle_control_authchallenge() 103
+problem function-size /src/feature/control/control_auth.c:handle_control_authenticate() 187
+problem function-size /src/feature/control/control_cmd.c:handle_control_extendcircuit() 151
+problem function-size /src/feature/control/control_cmd.c:handle_control_add_onion() 269
+problem function-size /src/feature/control/control_cmd.c:add_onion_helper_keyarg() 125
+problem function-size /src/feature/control/control_events.c:control_event_stream_status() 119
+problem include-count /src/feature/control/control_getinfo.c 54
+problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_misc() 109
+problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_dir() 304
+problem function-size /src/feature/control/control_getinfo.c:getinfo_helper_events() 236
+problem function-size /src/feature/dirauth/bwauth.c:dirserv_read_measured_bandwidths() 124
+problem file-size /src/feature/dirauth/dirvote.c 4726
+problem include-count /src/feature/dirauth/dirvote.c 53
+problem function-size /src/feature/dirauth/dirvote.c:format_networkstatus_vote() 249
+problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_bw_weights_v10() 235
+problem function-size /src/feature/dirauth/dirvote.c:networkstatus_compute_consensus() 962
+problem function-size /src/feature/dirauth/dirvote.c:networkstatus_add_detached_signatures() 123
+problem function-size /src/feature/dirauth/dirvote.c:dirvote_add_vote() 162
+problem function-size /src/feature/dirauth/dirvote.c:dirvote_compute_consensuses() 164
+problem function-size /src/feature/dirauth/dirvote.c:dirserv_generate_networkstatus_vote_obj() 293
+problem function-size /src/feature/dirauth/dsigs_parse.c:networkstatus_parse_detached_signatures() 196
+problem function-size /src/feature/dirauth/guardfraction.c:dirserv_read_guardfraction_file_from_str() 110
+problem function-size /src/feature/dirauth/process_descs.c:dirserv_add_descriptor() 125
+problem function-size /src/feature/dirauth/shared_random.c:should_keep_commit() 110
+problem function-size /src/feature/dirauth/voteflags.c:dirserv_compute_performance_thresholds() 172
+problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_cleanup() 115
+problem function-size /src/feature/dircache/consdiffmgr.c:consdiffmgr_rescan_flavor_() 111
+problem function-size /src/feature/dircache/consdiffmgr.c:consensus_diff_worker_threadfn() 132
+problem function-size /src/feature/dircache/dircache.c:handle_get_current_consensus() 166
+problem function-size /src/feature/dircache/dircache.c:directory_handle_command_post() 120
+problem file-size /src/feature/dirclient/dirclient.c 3215
+problem include-count /src/feature/dirclient/dirclient.c 51
+problem function-size /src/feature/dirclient/dirclient.c:directory_get_from_dirserver() 131
+problem function-size /src/feature/dirclient/dirclient.c:directory_initiate_request() 201
+problem function-size /src/feature/dirclient/dirclient.c:directory_send_command() 241
+problem function-size /src/feature/dirclient/dirclient.c:dir_client_decompress_response_body() 114
+problem function-size /src/feature/dirclient/dirclient.c:connection_dir_client_reached_eof() 189
+problem function-size /src/feature/dirclient/dirclient.c:handle_response_fetch_consensus() 105
+problem function-size /src/feature/dircommon/consdiff.c:gen_ed_diff() 204
+problem function-size /src/feature/dircommon/consdiff.c:apply_ed_diff() 159
+problem function-size /src/feature/dirparse/authcert_parse.c:authority_cert_parse_from_string() 182
+problem function-size /src/feature/dirparse/microdesc_parse.c:microdescs_parse_from_string() 169
+problem function-size /src/feature/dirparse/ns_parse.c:routerstatus_parse_entry_from_string() 286
+problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_verify_bw_weights() 389
+problem function-size /src/feature/dirparse/ns_parse.c:networkstatus_parse_vote_from_string() 638
+problem function-size /src/feature/dirparse/parsecommon.c:tokenize_string() 103
+problem function-size /src/feature/dirparse/parsecommon.c:get_next_token() 159
+problem function-size /src/feature/dirparse/routerparse.c:router_parse_entry_from_string() 557
+problem function-size /src/feature/dirparse/routerparse.c:extrainfo_parse_entry_from_string() 210
+problem function-size /src/feature/hibernate/hibernate.c:accounting_parse_options() 109
+problem function-size /src/feature/hs/hs_cell.c:hs_cell_build_establish_intro() 115
+problem function-size /src/feature/hs/hs_cell.c:hs_cell_parse_introduce2() 154
+problem function-size /src/feature/hs/hs_client.c:send_introduce1() 104
+problem function-size /src/feature/hs/hs_client.c:hs_config_client_authorization() 108
+problem function-size /src/feature/hs/hs_common.c:hs_get_responsible_hsdirs() 104
+problem function-size /src/feature/hs/hs_config.c:config_generic_service() 140
+problem function-size /src/feature/hs/hs_descriptor.c:desc_encode_v3() 104
+problem function-size /src/feature/hs/hs_descriptor.c:decrypt_desc_layer() 110
+problem function-size /src/feature/hs/hs_descriptor.c:decode_introduction_point() 122
+problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_superencrypted_v3() 109
+problem function-size /src/feature/hs/hs_descriptor.c:desc_decode_encrypted_v3() 109
+problem file-size /src/feature/hs/hs_service.c 4109
+problem function-size /src/feature/keymgt/loadkey.c:ed_key_init_from_file() 333
+problem function-size /src/feature/nodelist/authcert.c:trusted_dirs_load_certs_from_string() 124
+problem function-size /src/feature/nodelist/authcert.c:authority_certs_fetch_missing() 296
+problem function-size /src/feature/nodelist/fmt_routerstatus.c:routerstatus_format_entry() 166
+problem function-size /src/feature/nodelist/microdesc.c:microdesc_cache_rebuild() 134
+problem include-count /src/feature/nodelist/networkstatus.c 62
+problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_check_consensus_signature() 176
+problem function-size /src/feature/nodelist/networkstatus.c:networkstatus_set_current_consensus() 293
+problem function-size /src/feature/nodelist/node_select.c:router_pick_directory_server_impl() 123
+problem function-size /src/feature/nodelist/node_select.c:compute_weighted_bandwidths() 206
+problem function-size /src/feature/nodelist/node_select.c:router_pick_trusteddirserver_impl() 114
+problem function-size /src/feature/nodelist/nodelist.c:compute_frac_paths_available() 193
+problem file-size /src/feature/nodelist/routerlist.c 3238
+problem function-size /src/feature/nodelist/routerlist.c:router_rebuild_store() 148
+problem function-size /src/feature/nodelist/routerlist.c:router_add_to_routerlist() 169
+problem function-size /src/feature/nodelist/routerlist.c:routerlist_remove_old_routers() 121
+problem function-size /src/feature/nodelist/routerlist.c:update_consensus_router_descriptor_downloads() 136
+problem function-size /src/feature/nodelist/routerlist.c:update_extrainfo_downloads() 103
+problem function-size /src/feature/relay/dns.c:dns_resolve_impl() 134
+problem function-size /src/feature/relay/dns.c:configure_nameservers() 161
+problem function-size /src/feature/relay/dns.c:evdns_callback() 109
+problem file-size /src/feature/relay/router.c 3407
+problem include-count /src/feature/relay/router.c 56
+problem function-size /src/feature/relay/router.c:init_keys() 252
+problem function-size /src/feature/relay/router.c:get_my_declared_family() 114
+problem function-size /src/feature/relay/router.c:router_build_fresh_unsigned_routerinfo() 136
+problem function-size /src/feature/relay/router.c:router_dump_router_to_string() 371
+problem function-size /src/feature/relay/router.c:extrainfo_dump_to_string() 206
+problem function-size /src/feature/relay/routerkeys.c:load_ed_keys() 294
+problem function-size /src/feature/rend/rendcache.c:rend_cache_store_v2_desc_as_client() 193
+problem function-size /src/feature/rend/rendclient.c:rend_client_send_introduction() 220
+problem function-size /src/feature/rend/rendcommon.c:rend_encode_v2_descriptors() 225
+problem function-size /src/feature/rend/rendmid.c:rend_mid_establish_intro_legacy() 104
+problem function-size /src/feature/rend/rendparse.c:rend_parse_v2_service_descriptor() 187
+problem function-size /src/feature/rend/rendparse.c:rend_decrypt_introduction_points() 104
+problem function-size /src/feature/rend/rendparse.c:rend_parse_introduction_points() 131
+problem file-size /src/feature/rend/rendservice.c 4511
+problem function-size /src/feature/rend/rendservice.c:rend_service_prune_list_impl_() 107
+problem function-size /src/feature/rend/rendservice.c:rend_config_service() 164
+problem function-size /src/feature/rend/rendservice.c:rend_service_load_auth_keys() 178
+problem function-size /src/feature/rend/rendservice.c:rend_service_receive_introduction() 332
+problem function-size /src/feature/rend/rendservice.c:rend_service_parse_intro_for_v3() 115
+problem function-size /src/feature/rend/rendservice.c:rend_service_decrypt_intro() 115
+problem function-size /src/feature/rend/rendservice.c:rend_service_intro_has_opened() 126
+problem function-size /src/feature/rend/rendservice.c:rend_service_rendezvous_has_opened() 117
+problem function-size /src/feature/rend/rendservice.c:directory_post_to_hs_dir() 108
+problem function-size /src/feature/rend/rendservice.c:upload_service_descriptor() 111
+problem function-size /src/feature/rend/rendservice.c:rend_consider_services_intro_points() 170
+problem function-size /src/feature/stats/rephist.c:rep_hist_load_mtbf_data() 185
+problem function-size /src/feature/stats/rephist.c:rep_hist_format_exit_stats() 148
+problem function-size /src/lib/compress/compress.c:tor_compress_impl() 133
+problem function-size /src/lib/compress/compress_zstd.c:tor_zstd_compress_process() 126
+problem function-size /src/lib/container/smartlist.c:smartlist_bsearch_idx() 109
+problem function-size /src/lib/crypt_ops/crypto_rand.c:crypto_strongest_rand_syscall() 102
+problem function-size /src/lib/encoding/binascii.c:base64_encode() 107
+problem function-size /src/lib/encoding/confline.c:parse_config_line_from_str_verbose() 119
+problem function-size /src/lib/encoding/cstring.c:unescape_string() 108
+problem function-size /src/lib/fs/dir.c:check_private_dir() 231
+problem function-size /src/lib/log/log.c:parse_log_severity_config() 101
+problem function-size /src/lib/math/prob_distr.c:sample_uniform_interval() 145
+problem function-size /src/lib/net/address.c:tor_addr_parse_mask_ports() 198
+problem function-size /src/lib/net/address.c:tor_addr_compare_masked() 111
+problem function-size /src/lib/net/inaddr.c:tor_inet_pton() 107
+problem function-size /src/lib/net/resolve.c:tor_addr_lookup() 110
+problem function-size /src/lib/net/socketpair.c:tor_ersatz_socketpair() 102
+problem function-size /src/lib/osinfo/uname.c:get_uname() 116
+problem function-size /src/lib/process/process_unix.c:process_unix_exec() 220
+problem function-size /src/lib/process/process_win32.c:process_win32_exec() 133
+problem function-size /src/lib/process/process_win32.c:process_win32_create_pipe() 112
+problem function-size /src/lib/process/restrict.c:set_max_file_descriptors() 102
+problem function-size /src/lib/process/setuid.c:switch_id() 156
+problem function-size /src/lib/sandbox/sandbox.c:prot_strings() 104
+problem function-size /src/lib/string/scanf.c:tor_vsscanf() 112
+problem function-size /src/lib/tls/tortls_nss.c:tor_tls_context_new() 153
+problem function-size /src/lib/tls/tortls_openssl.c:tor_tls_context_new() 171
+problem function-size /src/lib/tls/x509_nss.c:tor_tls_create_certificate_internal() 126
+problem function-size /src/tools/tor-gencert.c:parse_commandline() 111
+problem function-size /src/tools/tor-resolve.c:build_socks5_resolve_request() 104
+problem function-size /src/tools/tor-resolve.c:do_resolve() 174
+problem function-size /src/tools/tor-resolve.c:main() 112
+
diff --git a/scripts/maint/practracker/metrics.py b/scripts/maint/practracker/metrics.py
new file mode 100644
index 0000000000..5fa305a868
--- /dev/null
+++ b/scripts/maint/practracker/metrics.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+# Implementation of various source code metrics.
+# These are currently ad-hoc string operations and regexps.
+# We might want to use a proper static analysis library in the future, if we want to get more advanced metrics.
+
+import re
+
+def get_file_len(f):
+ """Get file length of file"""
+ for i, l in enumerate(f):
+ pass
+ return i + 1
+
+def get_include_count(f):
+ """Get number of #include statements in the file"""
+ include_count = 0
+ for line in f:
+ if re.match(r' *# *include', line):
+ include_count += 1
+ return include_count
+
+def get_function_lines(f):
+ """
+ Return iterator which iterates over functions and returns (function name, function lines)
+ """
+
+ # Skip lines that look like they are defining functions with these
+ # names: they aren't real function definitions.
+ REGEXP_CONFUSE_TERMS = {"MOCK_IMPL", "ENABLE_GCC_WARNINGS", "ENABLE_GCC_WARNING", "DUMMY_TYPECHECK_INSTANCE",
+ "DISABLE_GCC_WARNING", "DISABLE_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)
+ if func_name in REGEXP_CONFUSE_TERMS:
+ continue
+ func_start = lineno
+ in_function = True
+
+ else:
+ # Find 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 0000000000..febb14639d
--- /dev/null
+++ b/scripts/maint/practracker/practracker.py
@@ -0,0 +1,216 @@
+#!/usr/bin/python
+
+"""
+Best-practices tracker for Tor source code.
+
+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, then log a problem about them.
+
+We currently do metrics about file size, function size and number of includes.
+
+practracker.py should be run with its second argument pointing to the Tor
+top-level source directory like this:
+ $ python3 ./scripts/maint/practracker/practracker.py .
+
+To regenerate the exceptions file so that it allows all current
+problems in the Tor source, use the --regen flag:
+ $ python3 --regen ./scripts/maint/practracker/practracker.py .
+"""
+
+from __future__ import print_function
+
+import os, sys
+
+import metrics
+import util
+import problem
+
+# The filename of the exceptions file (it should be placed in the practracker directory)
+EXCEPTIONS_FNAME = "./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
+
+#######################################################
+
+# ProblemVault singleton
+ProblemVault = None
+
+# The Tor source code topdir
+TOR_TOPDIR = None
+
+#######################################################
+
+if sys.version_info[0] <= 2:
+ def open_file(fname):
+ return open(fname, 'r')
+else:
+ def open_file(fname):
+ return open(fname, 'r', encoding='utf-8')
+
+def consider_file_size(fname, f):
+ """Consider file size issues for 'f' and return True if a new issue was found"""
+ file_size = metrics.get_file_len(f)
+ if file_size > MAX_FILE_SIZE:
+ p = problem.FileSizeProblem(fname, file_size)
+ return ProblemVault.register_problem(p)
+ return False
+
+def consider_includes(fname, f):
+ """Consider #include issues for 'f' and return True if a new issue was found"""
+ include_count = metrics.get_include_count(f)
+
+ if include_count > MAX_INCLUDE_COUNT:
+ p = problem.IncludeCountProblem(fname, include_count)
+ return ProblemVault.register_problem(p)
+ return False
+
+def consider_function_size(fname, f):
+ """Consider the function sizes for 'f' and return True if a new issue was found"""
+ found_new_issues = False
+
+ for name, lines in metrics.get_function_lines(f):
+ # Don't worry about functions within our limits
+ if lines <= MAX_FUNCTION_SIZE:
+ continue
+
+ # That's a big function! Issue a problem!
+ canonical_function_name = "%s:%s()" % (fname, name)
+ p = problem.FunctionSizeProblem(canonical_function_name, lines)
+ found_new_issues |= ProblemVault.register_problem(p)
+
+ return found_new_issues
+
+#######################################################
+
+def consider_all_metrics(files_list):
+ """Consider metrics for all files, and return True if new issues were found"""
+ found_new_issues = False
+ for fname in files_list:
+ with open_file(fname) as f:
+ found_new_issues |= consider_metrics_for_file(fname, f)
+ return found_new_issues
+
+def consider_metrics_for_file(fname, f):
+ """
+ Consider the various metrics for file with filename 'fname' and file descriptor 'f'.
+ Return True if we found new issues.
+ """
+ # Strip the useless part of the path
+ if fname.startswith(TOR_TOPDIR):
+ fname = fname[len(TOR_TOPDIR):]
+
+ found_new_issues = False
+
+ # Get file length
+ found_new_issues |= consider_file_size(fname, f)
+
+ # Consider number of #includes
+ f.seek(0)
+ found_new_issues |= consider_includes(fname, f)
+
+ # Get function length
+ f.seek(0)
+ found_new_issues |= consider_function_size(fname, f)
+
+ return found_new_issues
+
+HEADER="""\
+# Welcome to the exceptions file for Tor's best-practices tracker!
+#
+# Each line of this file represents a single violation of Tor's best
+# practices -- typically, a violation that we had before practracker.py
+# first existed.
+#
+# There are three kinds of problems that we recognize right now:
+# function-size -- a function of more than {MAX_FUNCTION_SIZE} lines.
+# file-size -- a file of more than {MAX_FILE_SIZE} lines.
+# include-count -- a file with more than {MAX_INCLUDE_COUNT} #includes.
+#
+# Each line below represents a single exception that practracker should
+# _ignore_. Each line has four parts:
+# 1. The word "problem".
+# 2. The kind of problem.
+# 3. The location of the problem: either a filename, or a
+# filename:functionname pair.
+# 4. The magnitude of the problem to ignore.
+#
+# So for example, consider this line:
+# problem file-size /src/core/or/connection_or.c 3200
+#
+# It tells practracker to allow the mentioned file to be up to 3200 lines
+# long, even though ordinarily it would warn about any file with more than
+# {MAX_FILE_SIZE} lines.
+#
+# You can either edit this file by hand, or regenerate it completely by
+# running `make practracker-regen`.
+#
+# Remember: It is better to fix the problem than to add a new exception!
+
+""".format(**globals())
+
+def main(argv):
+ import argparse
+
+ progname = argv[0]
+ parser = argparse.ArgumentParser(prog=progname)
+ parser.add_argument("--regen", action="store_true",
+ help="Regenerate the exceptions file")
+ parser.add_argument("--exceptions",
+ help="Override the location for the exceptions file")
+ parser.add_argument("topdir", default=".", nargs="?",
+ help="Top-level directory for the tor source")
+ args = parser.parse_args(argv[1:])
+
+ global TOR_TOPDIR
+ TOR_TOPDIR = args.topdir
+ if args.exceptions:
+ exceptions_file = args.exceptions
+ else:
+ exceptions_file = os.path.join(TOR_TOPDIR, "scripts/maint/practracker", EXCEPTIONS_FNAME)
+
+ # 1) Get all the .c files we care about
+ files_list = util.get_tor_c_files(TOR_TOPDIR)
+
+ # 2) Initialize problem vault and load an optional exceptions file so that
+ # we don't warn about the past
+ global ProblemVault
+
+ if args.regen:
+ tmpname = exceptions_file + ".tmp"
+ tmpfile = open(tmpname, "w")
+ sys.stdout = tmpfile
+ sys.stdout.write(HEADER)
+ ProblemVault = problem.ProblemVault()
+ else:
+ ProblemVault = problem.ProblemVault(exceptions_file)
+
+ # 3) Go through all the files and report problems if they are not exceptions
+ found_new_issues = consider_all_metrics(files_list)
+
+ if args.regen:
+ tmpfile.close()
+ os.rename(tmpname, exceptions_file)
+ sys.exit(0)
+
+ # If new issues were found, try to give out some advice to the developer on how to resolve it.
+ if found_new_issues and not args.regen:
+ new_issues_str = """\
+FAILURE: practracker found new problems in the code: see warnings above.
+
+Please fix the problems if you can, and update the exceptions file
+({}) if you can't.
+
+See doc/HACKING/HelpfulTools.md for more information on using practracker.\
+""".format(exceptions_file)
+ print(new_issues_str)
+
+ sys.exit(found_new_issues)
+
+if __name__ == '__main__':
+ main(sys.argv)
diff --git a/scripts/maint/practracker/practracker_tests.py b/scripts/maint/practracker/practracker_tests.py
new file mode 100755
index 0000000000..cdbab2908e
--- /dev/null
+++ b/scripts/maint/practracker/practracker_tests.py
@@ -0,0 +1,50 @@
+"""Some simple tests for practracker metrics"""
+
+import unittest
+
+import StringIO
+
+import metrics
+
+function_file = """static void
+fun(directory_request_t *req, const char *resource)
+{
+ time_t if_modified_since = 0;
+ uint8_t or_diff_from[DIGEST256_LEN];
+}
+
+static void
+fun(directory_request_t *req,
+ const char *resource)
+{
+ time_t if_modified_since = 0;
+ uint8_t or_diff_from[DIGEST256_LEN];
+}
+
+MOCK_IMPL(void,
+fun,(
+ uint8_t dir_purpose,
+ uint8_t router_purpose,
+ const char *resource,
+ int pds_flags,
+ download_want_authority_t want_authority))
+{
+ const routerstatus_t *rs = NULL;
+ const or_options_t *options = get_options();
+}
+"""
+
+class TestFunctionLength(unittest.TestCase):
+ def test_function_length(self):
+ funcs = StringIO.StringIO(function_file)
+ # All functions should have length 2
+ for name, lines in metrics.function_lines(funcs):
+ self.assertEqual(name, "fun")
+
+ funcs.seek(0)
+
+ for name, lines in metrics.function_lines(funcs):
+ self.assertEqual(lines, 2)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/scripts/maint/practracker/problem.py b/scripts/maint/practracker/problem.py
new file mode 100644
index 0000000000..c82c5db572
--- /dev/null
+++ b/scripts/maint/practracker/problem.py
@@ -0,0 +1,158 @@
+"""
+In this file we define a ProblemVault class where we store all the
+exceptions and all the problems we find with the code.
+
+The ProblemVault is capable of registering problems and also figuring out if a
+problem is worse than a registered exception so that it only warns when things
+get worse.
+"""
+
+from __future__ import print_function
+
+import os.path
+import re
+import sys
+
+class ProblemVault(object):
+ """
+ Singleton where we store the various new problems we
+ found in the code, and also the old problems we read from the exception
+ file.
+ """
+ def __init__(self, exception_fname=None):
+ # Exception dictionary: { problem.key() : Problem object }
+ self.exceptions = {}
+
+ if exception_fname == None:
+ return
+
+ try:
+ with open(exception_fname, 'r') as exception_f:
+ self.register_exceptions(exception_f)
+ except IOError:
+ print("No exception file provided", file=sys.stderr)
+
+ def register_exceptions(self, exception_file):
+ # Register exceptions
+ for lineno, line in enumerate(exception_file, 1):
+ try:
+ problem = get_old_problem_from_exception_str(line)
+ except ValueError as v:
+ print("Exception file line {} not recognized: {}"
+ .format(lineno,v),
+ file=sys.stderr)
+ continue
+
+ if problem is None:
+ continue
+
+ # Fail if we see dup exceptions. There is really no reason to have dup exceptions.
+ if problem.key() in self.exceptions:
+ print("Duplicate exceptions lines found in exception file:\n\t{}\n\t{}\nAborting...".format(problem, self.exceptions[problem.key()]),
+ file=sys.stderr)
+ sys.exit(1)
+
+ self.exceptions[problem.key()] = problem
+ #print "Registering exception: %s" % problem
+
+ def register_problem(self, problem):
+ """
+ Register this problem to the problem value. Return True if it was a new
+ problem or it worsens an already existing problem.
+ """
+ # This is a new problem, print it
+ if problem.key() not in self.exceptions:
+ print(problem)
+ return True
+
+ # If it's an old problem, we don't warn if the situation got better
+ # (e.g. we went from 4k LoC to 3k LoC), but we do warn if the
+ # situation worsened (e.g. we went from 60 includes to 80).
+ if problem.is_worse_than(self.exceptions[problem.key()]):
+ print(problem)
+ return True
+
+ return False
+
+class Problem(object):
+ """
+ A generic problem in our source code. See the subclasses below for the
+ specific problems we are trying to tackle.
+ """
+ def __init__(self, problem_type, problem_location, metric_value):
+ self.problem_location = problem_location
+ self.metric_value = int(metric_value)
+ self.problem_type = problem_type
+
+ def is_worse_than(self, other_problem):
+ """Return True if this is a worse problem than other_problem"""
+ if self.metric_value > other_problem.metric_value:
+ return True
+ return False
+
+ def key(self):
+ """Generate a unique key that describes this problem that can be used as a dictionary key"""
+ # Problem location is a filesystem path, so we need to normalize this
+ # across platforms otherwise same paths are not gonna match.
+ canonical_location = os.path.normcase(self.problem_location)
+ return "%s:%s" % (canonical_location, self.problem_type)
+
+ def __str__(self):
+ return "problem %s %s %s" % (self.problem_type, self.problem_location, self.metric_value)
+
+class FileSizeProblem(Problem):
+ """
+ Denotes a problem with the size of a .c file.
+
+ The 'problem_location' is the filesystem path of the .c file, and the
+ 'metric_value' is the number of lines in the .c file.
+ """
+ def __init__(self, problem_location, metric_value):
+ super(FileSizeProblem, self).__init__("file-size", problem_location, metric_value)
+
+class IncludeCountProblem(Problem):
+ """
+ Denotes a problem with the number of #includes in a .c file.
+
+ The 'problem_location' is the filesystem path of the .c file, and the
+ 'metric_value' is the number of #includes in the .c file.
+ """
+ def __init__(self, problem_location, metric_value):
+ super(IncludeCountProblem, self).__init__("include-count", problem_location, metric_value)
+
+class FunctionSizeProblem(Problem):
+ """
+ Denotes a problem with a size of a function in a .c file.
+
+ The 'problem_location' is "<path>:<function>()" where <path> is the
+ filesystem path of the .c file and <function> is the name of the offending
+ function.
+
+ The 'metric_value' is the size of the offending function in lines.
+ """
+ def __init__(self, problem_location, metric_value):
+ super(FunctionSizeProblem, self).__init__("function-size", problem_location, metric_value)
+
+comment_re = re.compile(r'#.*$')
+
+def get_old_problem_from_exception_str(exception_str):
+ orig_str = exception_str
+ exception_str = comment_re.sub("", exception_str)
+ fields = exception_str.split()
+ if len(fields) == 0:
+ # empty line or comment
+ return None
+ elif len(fields) == 4:
+ # valid line
+ _, problem_type, problem_location, metric_value = fields
+ else:
+ raise ValueError("Misformatted line {!r}".format(orig_str))
+
+ if problem_type == "file-size":
+ return FileSizeProblem(problem_location, metric_value)
+ elif problem_type == "include-count":
+ return IncludeCountProblem(problem_location, metric_value)
+ elif problem_type == "function-size":
+ return FunctionSizeProblem(problem_location, metric_value)
+ else:
+ raise ValueError("Unknown exception type {!r}".format(orig_str))
diff --git a/scripts/maint/practracker/util.py b/scripts/maint/practracker/util.py
new file mode 100644
index 0000000000..b0ca73b997
--- /dev/null
+++ b/scripts/maint/practracker/util.py
@@ -0,0 +1,28 @@
+import os
+
+# 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/"}
+
+def get_tor_c_files(tor_topdir):
+ """
+ Return a list with the .c filenames we want to get metrics of.
+ """
+ files_list = []
+
+ for root, directories, filenames in os.walk(tor_topdir):
+ directories.sort()
+ filenames.sort()
+ 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(os.path.normcase(exclude_dir) in full_path for exclude_dir in EXCLUDE_SOURCE_DIRS):
+ continue
+
+ files_list.append(full_path)
+
+ return files_list
diff --git a/scripts/maint/pre-commit.git-hook b/scripts/maint/pre-commit.git-hook
deleted file mode 100755
index b4c4ce2061..0000000000
--- a/scripts/maint/pre-commit.git-hook
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# To install this script, copy it to .git/hooks/pre-commit in local copy of
-# tor git repo and make sure it has permission to execute.
-#
-# This is pre-commit git hook script that prevents commiting your changeset if
-# it fails our code formatting or changelog entry formatting checkers.
-
-workdir=$(git rev-parse --show-toplevel)
-
-cd "$workdir" || exit 1
-
-python scripts/maint/lintChanges.py ./changes/*
-
-perl scripts/maint/checkSpace.pl -C \
-src/lib/*/*.[ch] \
-src/core/*/*.[ch] \
-src/feature/*/*.[ch] \
-src/app/*/*.[ch] \
-src/test/*.[ch] \
-src/test/*/*.[ch] \
-src/tools/*.[ch]
-
-if test -e scripts/maint/checkIncludes.py; then
- python scripts/maint/checkIncludes.py
-fi
diff --git a/scripts/maint/pre-push.git-hook b/scripts/maint/pre-push.git-hook
deleted file mode 100755
index 26296023fb..0000000000
--- a/scripts/maint/pre-push.git-hook
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-
-# To install this script, copy it into .git/hooks/pre-push path in your
-# local copy of git repository. Make sure it has permission to execute.
-#
-# This is git pre-push hook script to prevent "fixup!" and "squash!" commits
-# from ending up in upstream branches (master, release-* or maint-*).
-#
-# The following sample script was used as starting point:
-# https://github.com/git/git/blob/master/templates/hooks--pre-push.sample
-
-z40=0000000000000000000000000000000000000000
-
-CUR_BRANCH=$(git rev-parse --abbrev-ref HEAD)
-if [ "$CUR_BRANCH" != "master" ] && [[ $CUR_BRANCH != release-* ]] &&
- [[ $CUR_BRANCH != maint-* ]]
-then
- exit 0
-fi
-
-echo "Running pre-push hook"
-
-# shellcheck disable=SC2034
-while read -r local_ref local_sha remote_ref remote_sha
-do
- if [ "$local_sha" = $z40 ]
- then
- # Handle delete
- :
- else
- if [ "$remote_sha" = $z40 ]
- then
- # New branch, examine all commits
- range="$local_sha"
- else
- # Update to existing branch, examine new commits
- range="$remote_sha..$local_sha"
- fi
-
- # Check for fixup! commit
- commit=$(git rev-list -n 1 --grep '^fixup!' "$range")
- if [ -n "$commit" ]
- then
- echo >&2 "Found fixup! commit in $local_ref, not pushing"
- echo >&2 "If you really want to push this, use --no-verify."
- exit 1
- fi
-
- # Check for squash! commit
- commit=$(git rev-list -n 1 --grep '^squash!' "$range")
- if [ -n "$commit" ]
- then
- echo >&2 "Found squash! commit in $local_ref, not pushing"
- echo >&2 "If you really want to push this, use --no-verify."
- exit 1
- fi
- fi
-done
-
-exit 0
-
diff --git a/scripts/maint/rectify_include_paths.py b/scripts/maint/rectify_include_paths.py
index 401fadae6d..1140e8cd22 100755
--- a/scripts/maint/rectify_include_paths.py
+++ b/scripts/maint/rectify_include_paths.py
@@ -1,8 +1,12 @@
-#!/usr/bin/python3
+#!/usr/bin/python
import os
import os.path
import re
+import sys
+
+def warn(msg):
+ sys.stderr.write("WARNING: %s\n"%msg)
# Find all the include files, map them to their real names.
@@ -11,6 +15,8 @@ def exclude(paths, dirnames):
if p in dirnames:
dirnames.remove(p)
+DUPLICATE = object()
+
def get_include_map():
includes = { }
@@ -19,7 +25,10 @@ def get_include_map():
for fname in fnames:
if fname.endswith(".h"):
- assert fname not in includes
+ if fname in includes:
+ warn("Multiple headers named %s"%fname)
+ includes[fname] = DUPLICATE
+ continue
include = os.path.join(dirpath, fname)
assert include.startswith("src/")
includes[fname] = include[4:]
@@ -37,7 +46,7 @@ def fix_includes(inp, out, mapping):
if m:
include,hdr,rest = m.groups()
basehdr = get_base_header_name(hdr)
- if basehdr in mapping:
+ if basehdr in mapping and mapping[basehdr] is not DUPLICATE:
out.write('{}{}{}\n'.format(include,mapping[basehdr],rest))
continue
diff --git a/scripts/maint/updateCopyright.pl b/scripts/maint/updateCopyright.pl
index 36894b1baf..6800032f87 100755
--- a/scripts/maint/updateCopyright.pl
+++ b/scripts/maint/updateCopyright.pl
@@ -1,7 +1,9 @@
#!/usr/bin/perl -i -w -p
-$NEWYEAR=2019;
+@now = gmtime();
-s/Copyright(.*) (201[^9]), The Tor Project/Copyright$1 $2-${NEWYEAR}, The Tor Project/;
+$NEWYEAR=$now[5]+1900;
+
+s/Copyright([^-]*) (20[^-]*), The Tor Project/Copyright$1 $2-${NEWYEAR}, The Tor Project/;
s/Copyright(.*)-(20..), The Tor Project/Copyright$1-${NEWYEAR}, The Tor Project/;