blob: 76ab89628aae2475ecdaa573b3a3206d7b3b451e [file] [log] [blame]
Tom Anderson352981c252018-12-01 00:36:451#!/usr/bin/env python
2# Copyright 2018 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import json
7import os
8import re
9import subprocess
10import sys
11
12import common
13
Lei Zhangbdbca9142020-09-21 06:22:1214# A list of files that are allowed to have static initializers.
Yuke Liaoa1ad0ec2019-06-12 20:10:1115# If something adds a static initializer, revert it. We don't accept regressions
16# in static initializers.
Lei Zhangbdbca9142020-09-21 06:22:1217_LINUX_SI_FILE_ALLOWLIST = {
Yuke Liaoa1ad0ec2019-06-12 20:10:1118 'chrome': [
Hans Wennborg18026ae2019-08-22 19:07:4619 'InstrProfilingRuntime.cpp', # Only in coverage builds, not production.
Yuke Liaoa1ad0ec2019-06-12 20:10:1120 'atomicops_internals_x86.cc', # TODO(crbug.com/973551): Remove.
21 'debugallocation_shim.cc', # TODO(crbug.com/973552): Remove.
22 'iostream.cpp', # TODO(crbug.com/973554): Remove.
23 'spinlock.cc', # TODO(crbug.com/973556): Remove.
24 ],
25 'nacl_helper_bootstrap': [],
Tom Anderson352981c252018-12-01 00:36:4526}
Lei Zhangbdbca9142020-09-21 06:22:1227_LINUX_SI_FILE_ALLOWLIST['nacl_helper'] = _LINUX_SI_FILE_ALLOWLIST['chrome']
Tom Anderson352981c252018-12-01 00:36:4528
Lei Zhang81f5dd12020-09-24 12:59:2829# The lists for Chrome OS are conceptually the same as the Linux ones above.
30# If something adds a static initializer, revert it. We don't accept regressions
31# in static initializers.
32_CROS_SI_FILE_ALLOWLIST = {
33 'chrome': [
34 'InstrProfilingRuntime.cpp', # Only in coverage builds, not production.
35 'atomicops_internals_x86.cc', # TODO(crbug.com/973551): Remove.
36 'debugallocation_shim.cc', # TODO(crbug.com/973552): Remove.
37 'iostream.cpp', # TODO(crbug.com/973554): Remove.
38 'spinlock.cc', # TODO(crbug.com/973556): Remove.
Lei Zhang81f5dd12020-09-24 12:59:2839 'int256.cc', # TODO(crbug.com/537099): Remove.
Lei Zhang81f5dd12020-09-24 12:59:2840 'rpc.pb.cc', # TODO(crbug.com/537099): Remove.
Lei Zhang81f5dd12020-09-24 12:59:2841 ],
42 'nacl_helper_bootstrap': [],
43}
44_CROS_SI_FILE_ALLOWLIST['nacl_helper'] = _LINUX_SI_FILE_ALLOWLIST['chrome']
45
Lei Zhangbdbca9142020-09-21 06:22:1246# Mac can use this list when a dsym is available, otherwise it will fall back
Sajjad Mirza26561f32020-03-18 17:55:2047# to checking the count.
Lei Zhangbdbca9142020-09-21 06:22:1248_MAC_SI_FILE_ALLOWLIST = [
Sajjad Mirza26561f32020-03-18 17:55:2049 'InstrProfilingRuntime.cpp', # Only in coverage builds, not in production.
50 'sysinfo.cc', # Only in coverage builds, not in production.
51 'iostream.cpp', # Used to setup std::cin/cout/cerr.
52]
53
Tom Andersond9d71d72019-01-18 17:58:2654# A static initializer is needed on Mac for libc++ to set up std::cin/cout/cerr
Sajjad Mirza26561f32020-03-18 17:55:2055# before main() runs. Coverage CQ will have a dsym so only iostream.cpp needs
56# to be counted here.
57FALLBACK_EXPECTED_MAC_SI_COUNT = 1
Tom Anderson352981c252018-12-01 00:36:4558
59
60def run_process(command):
61 p = subprocess.Popen(command, stdout=subprocess.PIPE)
62 stdout = p.communicate()[0]
63 if p.returncode != 0:
64 raise Exception(
65 'ERROR from command "%s": %d' % (' '.join(command), p.returncode))
66 return stdout
67
68
69def main_mac(src_dir):
70 base_names = ('Chromium', 'Google Chrome')
71 ret = 0
72 for base_name in base_names:
73 app_bundle = base_name + '.app'
74 framework_name = base_name + ' Framework'
75 framework_bundle = framework_name + '.framework'
76 framework_dsym_bundle = framework_bundle + '.dSYM'
77 framework_unstripped_name = framework_name + '.unstripped'
78 chromium_executable = os.path.join(app_bundle, 'Contents', 'MacOS',
79 base_name)
80 chromium_framework_executable = os.path.join(framework_bundle,
81 framework_name)
82 chromium_framework_dsym = os.path.join(framework_dsym_bundle, 'Contents',
83 'Resources', 'DWARF', framework_name)
84 if os.path.exists(chromium_executable):
85 # Count the number of files with at least one static initializer.
86 si_count = 0
87 # Find the __DATA,__mod_init_func section.
Stephanie Kim10dd25ba2020-08-18 20:26:5788
89 # If the checkout uses the hermetic xcode binaries, then otool must be
90 # directly invoked. The indirection via /usr/bin/otool won't work unless
91 # there's an actual system install of Xcode.
92 hermetic_xcode_path = os.path.join(src_dir, 'build', 'mac_files',
93 'xcode_binaries')
94 if os.path.exists(hermetic_xcode_path):
95 otool_path = os.path.join(hermetic_xcode_path, 'Contents', 'Developer',
96 'Toolchains', 'XcodeDefault.xctoolchain', 'usr', 'bin', 'otool')
97 else:
98 otool_path = 'otool'
99
100 stdout = run_process([otool_path, '-l', chromium_framework_executable])
Tom Anderson352981c252018-12-01 00:36:45101 section_index = stdout.find('sectname __mod_init_func')
102 if section_index != -1:
103 # If the section exists, the "size" line must follow it.
104 initializers_s = re.search('size 0x([0-9a-f]+)',
105 stdout[section_index:]).group(1)
106 word_size = 8 # Assume 64 bit
107 si_count = int(initializers_s, 16) / word_size
108
109 # Print the list of static initializers.
Sajjad Mirza26561f32020-03-18 17:55:20110 if si_count > 0:
Tom Anderson352981c252018-12-01 00:36:45111 # First look for a dSYM to get information about the initializers. If
112 # one is not present, check if there is an unstripped copy of the build
113 # output.
114 mac_tools_path = os.path.join(src_dir, 'tools', 'mac')
115 if os.path.exists(chromium_framework_dsym):
116 dump_static_initializers = os.path.join(
117 mac_tools_path, 'dump-static-initializers.py')
118 stdout = run_process(
119 [dump_static_initializers, chromium_framework_dsym])
Sajjad Mirza26561f32020-03-18 17:55:20120 for line in stdout:
121 if re.match('0x[0-9a-f]+', line) and not any(
Lei Zhangbdbca9142020-09-21 06:22:12122 f in line for f in _MAC_SI_FILE_ALLOWLIST):
Sajjad Mirza26561f32020-03-18 17:55:20123 ret = 1
124 print 'Found invalid static initializer: {}'.format(line)
Tom Anderson352981c252018-12-01 00:36:45125 print stdout
Sajjad Mirza26561f32020-03-18 17:55:20126 elif si_count > FALLBACK_EXPECTED_MAC_SI_COUNT:
127 print('Expected <= %d static initializers in %s, but found %d' %
128 (FALLBACK_EXPECTED_MAC_SI_COUNT, chromium_framework_executable,
129 si_count))
Erik Staab9b93c642020-09-16 20:17:56130 ret = 1
Tom Anderson352981c252018-12-01 00:36:45131 show_mod_init_func = os.path.join(mac_tools_path,
132 'show_mod_init_func.py')
133 args = [show_mod_init_func]
134 if os.path.exists(framework_unstripped_name):
135 args.append(framework_unstripped_name)
136 else:
137 print '# Warning: Falling back to potentially stripped output.'
138 args.append(chromium_framework_executable)
Stephanie Kim10dd25ba2020-08-18 20:26:57139
140 if os.path.exists(hermetic_xcode_path):
141 args.extend(['--xcode-path', hermetic_xcode_path])
142
Tom Anderson352981c252018-12-01 00:36:45143 stdout = run_process(args)
144 print stdout
145 return ret
146
147
Lei Zhang81f5dd12020-09-24 12:59:28148def main_linux(src_dir, is_chromeos):
Tom Anderson352981c252018-12-01 00:36:45149 ret = 0
Lei Zhang81f5dd12020-09-24 12:59:28150 allowlist = _CROS_SI_FILE_ALLOWLIST if is_chromeos else \
151 _LINUX_SI_FILE_ALLOWLIST
152 for binary_name in allowlist:
Tom Anderson352981c252018-12-01 00:36:45153 if not os.path.exists(binary_name):
154 continue
Tom Anderson352981c252018-12-01 00:36:45155
Yuke Liaoa1ad0ec2019-06-12 20:10:11156 dump_static_initializers = os.path.join(src_dir, 'tools', 'linux',
157 'dump-static-initializers.py')
158 stdout = run_process([dump_static_initializers, '-d', binary_name])
159 # The output has the following format:
160 # First lines: '# <file_name> <si_name>'
161 # Last line: '# Found <num> static initializers in <num> files.'
162 #
163 # For example:
164 # # spinlock.cc GetSystemCPUsCount()
165 # # spinlock.cc adaptive_spin_count
166 # # Found 2 static initializers in 1 files.
167
168 files_with_si = set()
169 for line in stdout.splitlines()[:-1]:
170 parts = line.split(' ', 2)
171 assert len(parts) == 3 and parts[0] == '#'
172
173 files_with_si.add(parts[1])
174
175 for f in files_with_si:
Lei Zhang81f5dd12020-09-24 12:59:28176 if f not in allowlist[binary_name]:
Yuke Liaoa1ad0ec2019-06-12 20:10:11177 ret = 1
178 print('Error: file "%s" is not expected to have static initializers in'
179 ' binary "%s"') % (f, binary_name)
180
181 print '\n# Static initializers in %s:' % binary_name
182 print stdout
183
Tom Anderson352981c252018-12-01 00:36:45184 return ret
185
186
187def main_run(args):
188 if args.build_config_fs != 'Release':
189 raise Exception('Only release builds are supported')
190
191 src_dir = args.paths['checkout']
192 build_dir = os.path.join(src_dir, 'out', args.build_config_fs)
193 os.chdir(build_dir)
194
195 if sys.platform.startswith('darwin'):
196 rc = main_mac(src_dir)
197 elif sys.platform == 'linux2':
Lei Zhang81f5dd12020-09-24 12:59:28198 is_chromeos = 'buildername' in args.properties and \
199 'chromeos' in args.properties['buildername']
200 rc = main_linux(src_dir, is_chromeos)
Tom Anderson352981c252018-12-01 00:36:45201 else:
202 sys.stderr.write('Unsupported platform %s.\n' % repr(sys.platform))
203 return 2
204
205 json.dump({
206 'valid': rc == 0,
207 'failures': [],
208 }, args.output)
209
210 return rc
211
212
213def main_compile_targets(args):
214 if sys.platform.startswith('darwin'):
215 compile_targets = ['chrome']
216 elif sys.platform == 'linux2':
217 compile_targets = ['chrome', 'nacl_helper', 'nacl_helper_bootstrap']
218 else:
219 compile_targets = []
220
221 json.dump(compile_targets, args.output)
222
223 return 0
224
225
226if __name__ == '__main__':
227 funcs = {
228 'run': main_run,
229 'compile_targets': main_compile_targets,
230 }
231 sys.exit(common.run_script(sys.argv[1:], funcs))