summaryrefslogtreecommitdiffstats
path: root/file_cmds/mtree/fix_failure_locations.py
blob: a49906eff4b0baa628d15645ce6d9683161c8997 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/python

#
# This script is used to automatically fix up the location numbers in
# calls to RECORD_FAILURE().  When adding a new call to RECORD_FAILURE,
# write it like:
#	RECORD_FAILURE(0, ...);
# Don't put any white space between the open parenthesis, zero and comma.
# Once you have added the new calls to RECORD_FAILURE, then run this script,
# passing it the path to the directory, like this:
#	python3 mtree/fix_failure_locations.py mtree/
#
# This script will edit the files, changing the "0" to the next available
# location number.  It will also detect and complain if you have duplicate
# location numbers.
#
# DO NOT reuse location numbers!  It is best if locations are consistent across
# all versions that have that RECORD_FAILURE call.
#

import sys
import os
import re
from collections import defaultdict
from datetime import datetime,timezone

class LocationUpdater(object):
    epoch = datetime(2020, 6, 17, 23, 22, 46, 562458, tzinfo=timezone.utc)
    location_base = int((datetime.now(timezone.utc) - epoch).total_seconds() / 60)
    # Match the number in "RECORD_FAILURE(<number>,"
    fail_re = (re.compile('(?<=\\bRECORD_FAILURE\\()\\d+(?=,)'),re.compile('(?<=\\bRECORD_FAILURE_MSG\\()\\d+(?=,)'))
    
    def __init__(self, path):
        self.location = self.location_base
        self.path = path
        # Counters for how often each location number was found
        self.counts = defaultdict(int)
        self.locations_changed = 0
    
    # Replace the "0" in "RECORD_FAILURE(0," with next location number, in *.c
    def fixLocations(self):
        def replace_loc(match):
            location = int(match.group(0))
            if location == 0:
                # Replace location 0 with the next available location
                self.location += 1
                self.locations_changed += 1
                location = self.location
            # Count the number of times this location number was used
            self.counts[location] += 1
            # Return the (possibly updated) location number
            return str(location)
        rootpath = self.path
        for dirpath, dirnames, filenames in os.walk(rootpath):
            for filename in filenames:
                if filename.endswith(".c") or filename.endswith(".cpp"):
                    path = os.path.join(dirpath, filename)
                    content = open(path, "r").read()
                    for fail_re in self.fail_re:
                        if fail_re.search(content):
                            locations_changed_before = self.locations_changed
                            content = fail_re.sub(replace_loc, content)
                            if self.locations_changed != locations_changed_before:
                                # We updated a location number, so write the changed file
                                print("Updating file {}".format(path))
                                open(path,"w").write(content)
    
    def duplicates(self):
        # Return the list of keys whose count is greater than 1
        return [k for (k,v) in iter(self.counts.items()) if v > 1]

updater = LocationUpdater(sys.argv[1])
updater.fixLocations()
dups = updater.duplicates()
if len(dups):
    print("WARNING!  Duplicate location numbers: {}".format(dups))
    sys.exit(1)