42
42
import re
43
43
import subprocess
44
44
import sys
45
+ from collections import defaultdict
45
46
46
47
import buildconfig
47
48
@@ -77,11 +78,10 @@ def main():
77
78
args = parser .parse_args ()
78
79
79
80
# Run |nm|. Options:
80
- # -u: show only undefined symbols
81
81
# -C: demangle symbol names
82
82
# -A: show an object filename for each undefined symbol
83
83
nm = buildconfig .substs .get ("NM" ) or "nm"
84
- cmd = [nm , "-u" , "- C" , "-A" , args .file ]
84
+ cmd = [nm , "-C" , "-A" , args .file ]
85
85
lines = subprocess .check_output (
86
86
cmd , universal_newlines = True , stderr = subprocess .PIPE
87
87
).split ("\n " )
@@ -110,23 +110,48 @@ def main():
110
110
# This regexp matches the relevant lines in the output of |nm|, which look
111
111
# like the following.
112
112
#
113
- # js/src/libjs_static.a:Utility.o: U malloc
113
+ # js/src/libjs_static.a:Utility.o: U malloc
114
+ # js/src/libjs_static.a:Utility.o: 00000000000007e0 T js::SetSourceOptions(...)
114
115
#
115
- alloc_fns_re = r"([^:/ ]+):\s+U (" + r"|" .join (alloc_fns ) + r")"
116
+ nm_line_re = re .compile (r"([^:/ ]+):\s+[0-9a-fA-F]*\s+([TU]) (.*)" )
117
+ alloc_fns_re = re .compile (r"|" .join (alloc_fns ))
116
118
117
- # This tracks which allocation/free functions have been seen in
118
- # util/Utility.cpp.
119
- util_Utility_cpp = set ([])
119
+ # This tracks which allocation/free functions have been seen.
120
+ functions = defaultdict (set )
121
+ files = defaultdict (int )
122
+
123
+ # Files to ignore allocation/free functions from.
124
+ ignored_files = [
125
+ # Ignore implicit call to operator new in std::condition_variable_any.
126
+ #
127
+ # From intl/icu/source/common/umutex.h:
128
+ # On Linux, the default constructor of std::condition_variable_any
129
+ # produces an in-line reference to global operator new(), [...].
130
+ "umutex.o" ,
131
+ # Ignore allocations from decimal conversion functions inside mozglue.
132
+ "Decimal.o" ,
133
+ # Ignore use of std::string in regexp AST debug output.
134
+ "regexp-ast.o" ,
135
+ ]
136
+ all_ignored_files = set ((f , 1 ) for f in ignored_files )
120
137
121
138
# Would it be helpful to emit detailed line number information after a failure?
122
139
emit_line_info = False
123
140
141
+ prev_filename = None
124
142
for line in lines :
125
- m = re .search (alloc_fns_re , line )
143
+ m = nm_line_re .search (line )
126
144
if m is None :
127
145
continue
128
146
129
- filename = m .group (1 )
147
+ filename , symtype , fn = m .groups ()
148
+ if prev_filename != filename :
149
+ # When the same filename appears multiple times, separated by other
150
+ # file names, this denotes a different file. Thankfully, we can more
151
+ # or less safely assume that dir1/Foo.o and dir2/Foo.o are not going
152
+ # to be next to each other.
153
+ files [filename ] += 1
154
+ prev_filename = filename
130
155
131
156
# The stdc++compat library has an implicit call to operator new in
132
157
# thread::_M_start_thread.
@@ -147,30 +172,29 @@ def main():
147
172
if "ProfilingStack" in filename :
148
173
continue
149
174
150
- # Ignore implicit call to operator new in std::condition_variable_any.
151
- #
152
- # From intl/icu/source/common/umutex.h:
153
- # On Linux, the default constructor of std::condition_variable_any
154
- # produces an in-line reference to global operator new(), [...] .
155
- if filename == "umutex.o" :
156
- continue
157
-
158
- # Ignore allocations from decimal conversion functions inside mozglue.
159
- if filename == "Decimal.o" :
160
- continue
175
+ if symtype == "T" :
176
+ # We can't match intl/components files by file name because in
177
+ # non-unified builds they overlap with files in js/src.
178
+ # So we check symbols they define, and consider files with symbols
179
+ # in the mozilla::intl namespace to be those .
180
+ if fn . startswith ( "mozilla::intl::" ) :
181
+ all_ignored_files . add (( filename , files [ filename ]))
182
+ else :
183
+ m = alloc_fns_re . match ( fn )
184
+ if m :
185
+ functions [( filename , files [ filename ])]. add ( m . group ( 0 ))
161
186
162
- # Ignore allocations from the m-c intl/components implementations.
163
- if "intl_components" in filename :
164
- continue
187
+ util_Utility_cpp = functions . pop (( "Utility.o" , 1 ))
188
+ if ( "Utility.o" , 2 ) in functions :
189
+ fail ( "There should be only one Utility.o file" )
165
190
166
- # Ignore use of std::string in regexp AST debug output.
167
- if filename == "regexp-ast.o" :
168
- continue
191
+ for f , n in all_ignored_files :
192
+ functions .pop ((f , n ), None )
193
+ if f in ignored_files and (f , 2 ) in functions :
194
+ fail (f"There should be only one { f } file" )
169
195
170
- fn = m .group (2 )
171
- if filename == "Utility.o" :
172
- util_Utility_cpp .add (fn )
173
- else :
196
+ for (filename , n ) in sorted (functions ):
197
+ for fn in functions [(filename , n )]:
174
198
# An allocation is present in a non-special file. Fail!
175
199
fail ("'" + fn + "' present in " + filename )
176
200
# Try to give more precise information about the offending code.
0 commit comments