Backed out changeset 9b91c7e0aa11 (bug 946813) for xpcshell crashes. a=backout
[gecko.git] / config / check_vanilla_allocations.py
blob74ea5e61d0c0f7e592aeaef5744d1bdee12b6522
1 # vim: set ts=8 sts=4 et sw=4 tw=79:
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 #----------------------------------------------------------------------------
7 # All heap allocations in SpiderMonkey must go through js_malloc, js_calloc,
8 # js_realloc, and js_free. This is so that any embedder who uses a custom
9 # allocator (by defining JS_USE_CUSTOM_ALLOCATOR) will see all heap allocation
10 # go through that custom allocator.
12 # Therefore, the presence of any calls to "vanilla" allocation/free functions
13 # (e.g. malloc(), free()) is a bug.
15 # This script checks for the presence of such disallowed vanilla
16 # allocation/free function in SpiderMonkey when it's built as a library. It
17 # relies on |nm| from the GNU binutils, and so only works on Linux, but one
18 # platform is good enough to catch almost all violations.
20 # This checking is only 100% reliable in a JS_USE_CUSTOM_ALLOCATOR build in
21 # which the default definitions of js_malloc et al (in Utility.h) -- which call
22 # malloc et al -- are replaced with empty definitions. This is because the
23 # presence and possible inlining of the default js_malloc et al can cause
24 # malloc/calloc/realloc/free calls show up in unpredictable places.
26 # Unfortunately, that configuration cannot be tested on Mozilla's standard
27 # testing infrastructure. Instead, by default this script only tests that none
28 # of the other vanilla allocation/free functions (operator new, memalign, etc)
29 # are present. If given the --aggressive flag, it will also check for
30 # malloc/calloc/realloc/free.
32 # Note: We don't check for |operator delete| and |operator delete[]|. These
33 # can be present somehow due to virtual destructors, but this is not too
34 # because vanilla delete/delete[] calls don't make sense without corresponding
35 # vanilla new/new[] calls, and any explicit calls will be caught by Valgrind's
36 # mismatched alloc/free checking.
37 #----------------------------------------------------------------------------
39 from __future__ import print_function
41 import argparse
42 import re
43 import subprocess
44 import sys
46 # The obvious way to implement this script is to search for occurrences of
47 # malloc et al, succeed if none are found, and fail is some are found.
48 # However, "none are found" does not necessarily mean "none are present" --
49 # this script could be buggy. (Or the output format of |nm| might change in
50 # the future.)
52 # So jsutil.cpp deliberately contains a (never-called) function that contains a
53 # single use of all the vanilla allocation/free functions. And this script
54 # fails if it (a) finds uses of those functions in files other than jsutil.cpp,
55 # *or* (b) fails to find them in jsutil.cpp.
57 # Tracks overall success of the test.
58 has_failed = False
61 def fail(msg):
62 print('TEST-UNEXPECTED-FAIL | check_vanilla_allocations.py |', msg)
63 global has_failed
64 has_failed = True
67 def main():
68 parser = argparse.ArgumentParser()
69 parser.add_argument('--aggressive', action='store_true',
70 help='also check for malloc, calloc, realloc and free')
71 parser.add_argument('file', type=str,
72 help='name of the file to check')
73 args = parser.parse_args()
75 # Run |nm|. Options:
76 # -u: show only undefined symbols
77 # -C: demangle symbol names
78 # -l: show a filename and line number for each undefined symbol
79 cmd = ['nm', '-u', '-C', '-l', args.file]
80 lines = subprocess.check_output(cmd, universal_newlines=True,
81 stderr=subprocess.PIPE).split('\n')
83 # alloc_fns contains all the vanilla allocation/free functions that we look
84 # for. Regexp chars are escaped appropriately.
86 alloc_fns = [
87 # Matches |operator new(unsigned T)|, where |T| is |int| or |long|.
88 r'operator new\(unsigned',
90 # Matches |operator new[](unsigned T)|, where |T| is |int| or |long|.
91 r'operator new\[\]\(unsigned',
93 r'memalign',
94 # These two aren't available on all Linux configurations.
95 #r'posix_memalign',
96 #r'aligned_alloc',
97 r'valloc',
98 r'strdup'
101 if args.aggressive:
102 alloc_fns += [
103 r'malloc',
104 r'calloc',
105 r'realloc',
106 r'free'
109 # This is like alloc_fns, but regexp chars are not escaped.
110 alloc_fns_unescaped = [fn.translate(None, r'\\') for fn in alloc_fns]
112 # This regexp matches the relevant lines in the output of |nm|, which look
113 # like the following.
115 # U malloc /path/to/objdir/dist/include/js/Utility.h:142
117 alloc_fns_re = r'U (' + r'|'.join(alloc_fns) + r').*\/([\w\.]+):(\d+)$'
119 # This tracks which allocation/free functions have been seen in jsutil.cpp.
120 jsutil_cpp = set([])
122 for line in lines:
123 m = re.search(alloc_fns_re, line)
124 if m is None:
125 continue
127 fn = m.group(1)
128 filename = m.group(2)
129 linenum = m.group(3)
130 if filename == 'jsutil.cpp':
131 jsutil_cpp.add(fn)
132 else:
133 # An allocation is present in a non-special file. Fail!
134 fail("'" + fn + "' present at " + filename + ':' + linenum)
137 # Check that all functions we expect are used in jsutil.cpp. (This will
138 # fail if the function-detection code breaks at any point.)
139 for fn in alloc_fns_unescaped:
140 if fn not in jsutil_cpp:
141 fail("'" + fn + "' isn't used as expected in jsutil.cpp")
142 else:
143 jsutil_cpp.remove(fn)
145 # This should never happen, but check just in case.
146 if jsutil_cpp:
147 fail('unexpected allocation fns used in jsutil.cpp: ' +
148 ', '.join(jsutil_cpp))
150 if has_failed:
151 sys.exit(1)
153 print('TEST-PASS | check_vanilla_allocations.py | ok')
154 sys.exit(0)
157 if __name__ == '__main__':
158 main()