blob: f24cfb7504cc8c27ec3b093e2cd865578dbfb0a8 [file] [log] [blame]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -08001# python3
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -08002# Copyright (C) 2019 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Grep warnings messages and output HTML tables or warning counts in CSV.
17
18Default is to output warnings in HTML tables grouped by warning severity.
19Use option --byproject to output tables grouped by source file projects.
20Use option --gencsv to output warning counts in CSV format.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080021
22Default input file is build.log, which can be changed with the --log flag.
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080023"""
24
25# List of important data structures and functions in this script.
26#
27# To parse and keep warning message in the input file:
28# severity: classification of message severity
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080029# warn_patterns:
30# warn_patterns[w]['category'] tool that issued the warning, not used now
31# warn_patterns[w]['description'] table heading
32# warn_patterns[w]['members'] matched warnings from input
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080033# warn_patterns[w]['patterns'] regular expressions to match warnings
34# warn_patterns[w]['projects'][p] number of warnings of pattern w in p
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080035# warn_patterns[w]['severity'] severity tuple
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080036# project_list[p][0] project name
37# project_list[p][1] regular expression to match a project path
38# project_patterns[p] re.compile(project_list[p][1])
39# project_names[p] project_list[p][0]
40# warning_messages array of each warning message, without source url
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080041# warning_links array of each warning code search link; for 'chrome'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080042# warning_records array of [idx to warn_patterns,
43# idx to project_names,
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080044# idx to warning_messages,
45# idx to warning_links]
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -080046# parse_input_file
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080047#
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080048import argparse
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080049import io
50import multiprocessing
51import os
52import re
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080053import sys
54
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -070055# pylint:disable=relative-beyond-top-level,no-name-in-module
56# suppress false positive of no-name-in-module warnings
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080057from . import android_project_list
58from . import chrome_project_list
59from . import cpp_warn_patterns as cpp_patterns
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -080060from . import html_writer
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080061from . import java_warn_patterns as java_patterns
62from . import make_warn_patterns as make_patterns
63from . import other_warn_patterns as other_patterns
64from . import tidy_warn_patterns as tidy_patterns
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080065
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -080066
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080067def parse_args(use_google3):
68 """Define and parse the args. Return the parse_args() result."""
69 parser = argparse.ArgumentParser(
70 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
71 parser.add_argument('--capacitor_path', default='',
72 help='Save capacitor warning file to the passed absolute'
73 ' path')
74 # csvpath has a different naming than the above path because historically the
75 # original Android script used csvpath, so other scripts rely on it
76 parser.add_argument('--csvpath', default='',
77 help='Save CSV warning file to the passed path')
78 parser.add_argument('--gencsv', action='store_true',
79 help='Generate CSV file with number of various warnings')
Saeid Farivar Asanjan75dc8d22020-11-18 00:29:43 +000080 parser.add_argument('--csvwithdescription', default='',
81 help="""Save CSV warning file to the passed path this csv
82 will contain all the warning descriptions""")
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -080083 parser.add_argument('--byproject', action='store_true',
84 help='Separate warnings in HTML output by project names')
85 parser.add_argument('--url', default='',
86 help='Root URL of an Android source code tree prefixed '
87 'before files in warnings')
88 parser.add_argument('--separator', default='?l=',
89 help='Separator between the end of a URL and the line '
90 'number argument. e.g. #')
91 parser.add_argument('--processes', default=multiprocessing.cpu_count(),
92 type=int,
93 help='Number of parallel processes to process warnings')
94 # Old Android build scripts call warn.py without --platform,
95 # so the default platform is set to 'android'.
96 parser.add_argument('--platform', default='android',
97 choices=['chrome', 'android'],
98 help='Platform of the build log')
99 # Old Android build scripts call warn.py with only a build.log file path.
100 parser.add_argument('--log', help='Path to build log file')
101 parser.add_argument(dest='buildlog', metavar='build.log',
102 default='build.log', nargs='?',
103 help='Path to build.log file')
104 flags = parser.parse_args()
105 if not flags.log:
106 flags.log = flags.buildlog
107 if not use_google3 and not os.path.exists(flags.log):
108 sys.exit('Cannot find log file: ' + flags.log)
109 return flags
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800110
111
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800112def get_project_names(project_list):
113 """Get project_names from project_list."""
114 return [p[0] for p in project_list]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800115
116
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800117def find_project_index(line, project_patterns):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700118 """Return the index to the project pattern array."""
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700119 for idx, pattern in enumerate(project_patterns):
120 if pattern.match(line):
121 return idx
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800122 return -1
123
124
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800125def classify_one_warning(warning, link, results, project_patterns,
126 warn_patterns):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800127 """Classify one warning line."""
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700128 for idx, pattern in enumerate(warn_patterns):
129 for cpat in pattern['compiled_patterns']:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800130 if cpat.match(warning):
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700131 project_idx = find_project_index(warning, project_patterns)
132 results.append([warning, link, idx, project_idx])
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800133 return
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700134 # If we end up here, there was a problem parsing the log
135 # probably caused by 'make -j' mixing the output from
136 # 2 or more concurrent compiles
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800137
138
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700139def remove_prefix(src, sub):
140 """Remove everything before last occurrence of substring sub in string src."""
141 if sub in src:
142 inc_sub = src.rfind(sub)
143 return src[inc_sub:]
144 return src
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800145
146
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800147# TODO(emmavukelj): Don't have any generate_*_cs_link functions call
148# normalize_path a second time (the first time being in parse_input_file)
149def generate_cs_link(warning_line, flags, android_root=None):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700150 """Try to add code search HTTP URL prefix."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800151 if flags.platform == 'chrome':
152 return generate_chrome_cs_link(warning_line, flags)
153 if flags.platform == 'android':
154 return generate_android_cs_link(warning_line, flags, android_root)
155 return 'https://cs.corp.google.com/'
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800156
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800157
158def generate_android_cs_link(warning_line, flags, android_root):
159 """Generate the code search link for a warning line in Android."""
160 # max_splits=2 -> only 3 items
161 raw_path, line_number_str, _ = warning_line.split(':', 2)
162 normalized_path = normalize_path(raw_path, flags, android_root)
163 if not flags.url:
164 return normalized_path
165 link_path = flags.url + '/' + normalized_path
166 if line_number_str.isdigit():
167 link_path += flags.separator + line_number_str
168 return link_path
169
170
171def generate_chrome_cs_link(warning_line, flags):
172 """Generate the code search link for a warning line in Chrome."""
173 split_line = warning_line.split(':')
174 raw_path = split_line[0]
175 normalized_path = normalize_path(raw_path, flags)
176 link_base = 'https://cs.chromium.org/'
177 link_add = 'chromium'
178 link_path = None
179
180 # Basically just going through a few specific directory cases and specifying
181 # the proper behavior for that case. This list of cases was accumulated
182 # through trial and error manually going through the warnings.
183 #
184 # This code pattern of using case-specific "if"s instead of "elif"s looks
185 # possibly accidental and mistaken but it is intentional because some paths
186 # fall under several cases (e.g. third_party/lib/nghttp2_frame.c) and for
187 # those we want the most specific case to be applied. If there is reliable
188 # knowledge of exactly where these occur, this could be changed to "elif"s
189 # but there is no reliable set of paths falling under multiple cases at the
190 # moment.
191 if '/src/third_party' in raw_path:
192 link_path = remove_prefix(raw_path, '/src/third_party/')
193 if '/chrome_root/src_internal/' in raw_path:
194 link_path = remove_prefix(raw_path, '/chrome_root/src_internal/')
195 link_path = link_path[len('/chrome_root'):] # remove chrome_root
196 if '/chrome_root/src/' in raw_path:
197 link_path = remove_prefix(raw_path, '/chrome_root/src/')
198 link_path = link_path[len('/chrome_root'):] # remove chrome_root
199 if '/libassistant/' in raw_path:
200 link_add = 'eureka_internal/chromium/src'
201 link_base = 'https://cs.corp.google.com/' # internal data
202 link_path = remove_prefix(normalized_path, '/libassistant/')
203 if raw_path.startswith('gen/'):
204 link_path = '/src/out/Debug/gen/' + normalized_path
205 if '/gen/' in raw_path:
206 return '%s?q=file:%s' % (link_base, remove_prefix(normalized_path, '/gen/'))
207
208 if not link_path and (raw_path.startswith('src/') or
209 raw_path.startswith('src_internal/')):
210 link_path = '/%s' % raw_path
211
212 if not link_path: # can't find specific link, send a query
213 return '%s?q=file:%s' % (link_base, normalized_path)
214
215 line_number = int(split_line[1])
216 link = '%s%s%s?l=%d' % (link_base, link_add, link_path, line_number)
217 return link
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800218
219
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800220def find_warn_py_and_android_root(path):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800221 """Return android source root path if warn.py is found."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800222 parts = path.split('/')
223 for idx in reversed(range(2, len(parts))):
224 root_path = '/'.join(parts[:idx])
225 # Android root directory should contain this script.
226 if os.path.exists(root_path + '/build/make/tools/warn.py'):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800227 return root_path
228 return ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800229
230
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700231def find_android_root_top_dirs(root_dir):
232 """Return a list of directories under the root_dir, if it exists."""
233 if not os.path.isdir(root_dir):
234 return None
235 return list(filter(lambda d: os.path.isdir(root_dir + '/' + d),
236 os.listdir(root_dir)))
237
238
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800239def find_android_root(buildlog):
240 """Guess android source root from common prefix of file paths."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800241 # Use the longest common prefix of the absolute file paths
242 # of the first 10000 warning messages as the android_root.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800243 warning_lines = []
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800244 warning_pattern = re.compile('^/[^ ]*/[^ ]*: warning: .*')
245 count = 0
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800246 for line in buildlog:
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700247 # We want to find android_root of a local build machine.
248 # Do not use RBE warning lines, which has '/b/f/w/' path prefix.
249 # Do not use /tmp/ file warnings.
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700250 if ('/b/f/w' not in line and not line.startswith('/tmp/') and
251 warning_pattern.match(line)):
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800252 warning_lines.append(line)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800253 count += 1
254 if count > 9999:
255 break
256 # Try to find warn.py and use its location to find
257 # the source tree root.
258 if count < 100:
259 path = os.path.normpath(re.sub(':.*$', '', line))
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800260 android_root = find_warn_py_and_android_root(path)
261 if android_root:
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700262 return android_root, find_android_root_top_dirs(android_root)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800263 # Do not use common prefix of a small number of paths.
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700264 android_root = ''
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800265 if count > 10:
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800266 # pytype: disable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800267 root_path = os.path.commonprefix(warning_lines)
Chih-Hung Hsieh949205a2020-01-10 10:33:40 -0800268 # pytype: enable=wrong-arg-types
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800269 if len(root_path) > 2 and root_path[len(root_path) - 1] == '/':
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700270 android_root = root_path[:-1]
271 if android_root and os.path.isdir(android_root):
272 return android_root, find_android_root_top_dirs(android_root)
273 # When the build.log file is moved to a different machine where
274 # android_root is not found, use the location of this script
275 # to find the android source tree root and its sub directories.
276 # This __file__ is /..../build/make/tools/warn/warn_common.py
277 script_root = __file__.replace('/build/make/tools/warn/warn_common.py', '')
278 return android_root, find_android_root_top_dirs(script_root)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800279
280
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800281def remove_android_root_prefix(path, android_root):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800282 """Remove android_root prefix from path if it is found."""
283 if path.startswith(android_root):
284 return path[1 + len(android_root):]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800285 return path
286
287
288def normalize_path(path, flags, android_root=None):
289 """Normalize file path relative to src/ or src-internal/ directory."""
290 path = os.path.normpath(path)
291
292 if flags.platform == 'android':
293 if android_root:
294 return remove_android_root_prefix(path, android_root)
295 return path
296
297 # Remove known prefix of root path and normalize the suffix.
298 idx = path.find('chrome_root/')
299 if idx >= 0:
300 # remove chrome_root/, we want path relative to that
301 return path[idx + len('chrome_root/'):]
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700302 return path
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800303
304
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800305def normalize_warning_line(line, flags, android_root=None):
306 """Normalize file path relative to src directory in a warning line."""
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800307 line = re.sub(u'[\u2018\u2019]', '\'', line)
308 # replace non-ASCII chars to spaces
309 line = re.sub(u'[^\x00-\x7f]', ' ', line)
310 line = line.strip()
311 first_column = line.find(':')
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800312 return normalize_path(line[:first_column], flags,
313 android_root) + line[first_column:]
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800314
315
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800316def parse_input_file_chrome(infile, flags):
317 """Parse Chrome input file, collect parameters and warning lines."""
318 platform_version = 'unknown'
319 board_name = 'unknown'
320 architecture = 'unknown'
321
322 # only handle warning lines of format 'file_path:line_no:col_no: warning: ...'
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700323 # Bug: http://198657613, This might need change to handle RBE output.
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800324 chrome_warning_pattern = r'^[^ ]*/[^ ]*:[0-9]+:[0-9]+: warning: .*'
325
326 warning_pattern = re.compile(chrome_warning_pattern)
327
328 # Collect all unique warning lines
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800329 unique_warnings = dict()
330 for line in infile:
331 if warning_pattern.match(line):
332 normalized_line = normalize_warning_line(line, flags)
333 if normalized_line not in unique_warnings:
334 unique_warnings[normalized_line] = generate_cs_link(line, flags)
335 elif (platform_version == 'unknown' or board_name == 'unknown' or
336 architecture == 'unknown'):
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700337 result = re.match(r'.+Package:.+chromeos-base/chromeos-chrome-', line)
338 if result is not None:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800339 platform_version = 'R' + line.split('chrome-')[1].split('_')[0]
340 continue
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700341 result = re.match(r'.+Source\sunpacked\sin\s(.+)', line)
342 if result is not None:
343 board_name = result.group(1).split('/')[2]
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800344 continue
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700345 result = re.match(r'.+USE:\s*([^\s]*).*', line)
346 if result is not None:
347 architecture = result.group(1)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800348 continue
349
350 header_str = '%s - %s - %s' % (platform_version, board_name, architecture)
351 return unique_warnings, header_str
352
353
354def add_normalized_line_to_warnings(line, flags, android_root, unique_warnings):
355 """Parse/normalize path, updating warning line and add to warnings dict."""
356 normalized_line = normalize_warning_line(line, flags, android_root)
357 if normalized_line not in unique_warnings:
358 unique_warnings[normalized_line] = generate_cs_link(line, flags,
359 android_root)
360 return unique_warnings
361
362
363def parse_input_file_android(infile, flags):
364 """Parse Android input file, collect parameters and warning lines."""
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700365 # pylint:disable=too-many-locals,too-many-branches
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800366 platform_version = 'unknown'
367 target_product = 'unknown'
368 target_variant = 'unknown'
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700369 build_id = 'unknown'
370 use_rbe = False
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700371 android_root, root_top_dirs = find_android_root(infile)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800372 infile.seek(0)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800373
374 # rustc warning messages have two lines that should be combined:
375 # warning: description
376 # --> file_path:line_number:column_number
377 # Some warning messages have no file name:
378 # warning: macro replacement list ... [bugprone-macro-parentheses]
379 # Some makefile warning messages have no line number:
380 # some/path/file.mk: warning: description
381 # C/C++ compiler warning messages have line and column numbers:
382 # some/path/file.c:line_number:column_number: warning: description
383 warning_pattern = re.compile('(^[^ ]*/[^ ]*: warning: .*)|(^warning: .*)')
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800384 rustc_file_position = re.compile('^[ ]+--> [^ ]*/[^ ]*:[0-9]+:[0-9]+')
385
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700386 # If RBE was used, try to reclaim some warning lines (from stdout)
387 # that contain leading characters from stderr.
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700388 # The leading characters can be any character, including digits and spaces.
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700389
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700390 # If a warning line's source file path contains the special RBE prefix
391 # /b/f/w/, we can remove all leading chars up to and including the "/b/f/w/".
392 bfw_warning_pattern = re.compile('.*/b/f/w/([^ ]*: warning: .*)')
393
394 # When android_root is known and available, we find its top directories
395 # and remove all leading chars before a top directory name.
396 # We assume that the leading chars from stderr do not contain "/".
397 # For example,
398 # 10external/...
399 # 12 warningsexternal/...
400 # 413 warningexternal/...
401 # 5 warnings generatedexternal/...
402 # Suppressed 1000 warnings (packages/modules/...
403 if root_top_dirs:
404 extra_warning_pattern = re.compile(
405 '^.[^/]*((' + '|'.join(root_top_dirs) +
406 ')/[^ ]*: warning: .*)')
407 else:
408 extra_warning_pattern = re.compile('^[^/]* ([^ /]*/[^ ]*: warning: .*)')
409
410 # Collect all unique warning lines
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800411 unique_warnings = dict()
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700412 checked_warning_lines = dict()
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800413 line_counter = 0
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800414 prev_warning = ''
415 for line in infile:
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700416 line_counter += 1
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800417 if prev_warning:
418 if rustc_file_position.match(line):
419 # must be a rustc warning, combine 2 lines into one warning
420 line = line.strip().replace('--> ', '') + ': ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800421 unique_warnings = add_normalized_line_to_warnings(
422 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800423 prev_warning = ''
424 continue
425 # add prev_warning, and then process the current line
426 prev_warning = 'unknown_source_file: ' + prev_warning
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800427 unique_warnings = add_normalized_line_to_warnings(
428 prev_warning, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800429 prev_warning = ''
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800430
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700431 # re.match is slow, with several warning line patterns and
432 # long input lines like "TIMEOUT: ...".
433 # We save significant time by skipping non-warning lines.
434 # But do not skip the first 100 lines, because we want to
435 # catch build variables.
436 if line_counter > 100 and line.find('warning: ') < 0:
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700437 continue
438
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700439 # A large clean build output can contain up to 90% of duplicated
440 # "warning:" lines. If we can skip them quickly, we can
441 # speed up this for-loop 3X to 5X.
442 if line in checked_warning_lines:
443 continue
444 checked_warning_lines[line] = True
445
446 # Clean up extra prefix if RBE is used.
447 if use_rbe:
448 if '/b/f/w/' in line:
449 result = bfw_warning_pattern.search(line)
450 else:
451 result = extra_warning_pattern.search(line)
452 if result is not None:
453 line = result.group(1)
454
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800455 if warning_pattern.match(line):
Chih-Hung Hsieha55837c2022-05-11 01:11:46 -0700456 if line.startswith('warning: '):
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800457 # save this line and combine it with the next line
458 prev_warning = line
459 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800460 unique_warnings = add_normalized_line_to_warnings(
461 line, flags, android_root, unique_warnings)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800462 continue
Chih-Hung Hsieh5392cdb2020-01-13 14:05:17 -0800463
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800464 if line_counter < 100:
465 # save a little bit of time by only doing this for the first few lines
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700466 result = re.search('(?<=^PLATFORM_VERSION=).*', line)
467 if result is not None:
468 platform_version = result.group(0)
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700469 continue
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700470 result = re.search('(?<=^TARGET_PRODUCT=).*', line)
471 if result is not None:
472 target_product = result.group(0)
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700473 continue
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700474 result = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
475 if result is not None:
476 target_variant = result.group(0)
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700477 continue
478 result = re.search('(?<=^BUILD_ID=).*', line)
479 if result is not None:
480 build_id = result.group(0)
481 continue
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700482 result = re.search('(?<=^TOP=).*', line)
483 if result is not None:
484 android_root = result.group(1)
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700485 continue
486 if re.search('USE_RBE=', line) is not None:
487 use_rbe = True
488 continue
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800489
490 if android_root:
491 new_unique_warnings = dict()
492 for warning_line in unique_warnings:
493 normalized_line = normalize_warning_line(warning_line, flags,
494 android_root)
495 new_unique_warnings[normalized_line] = generate_android_cs_link(
496 warning_line, flags, android_root)
497 unique_warnings = new_unique_warnings
498
Chih-Hung Hsieh77e31462021-09-02 16:37:23 -0700499 header_str = '%s - %s - %s (%s)' % (
500 platform_version, target_product, target_variant, build_id)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800501 return unique_warnings, header_str
502
503
504def parse_input_file(infile, flags):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700505 """Parse one input file for chrome or android."""
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800506 if flags.platform == 'chrome':
507 return parse_input_file_chrome(infile, flags)
508 if flags.platform == 'android':
509 return parse_input_file_android(infile, flags)
510 raise RuntimeError('parse_input_file not defined for platform %s' %
511 flags.platform)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800512
513
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800514def parse_compiler_output(compiler_output):
515 """Parse compiler output for relevant info."""
516 split_output = compiler_output.split(':', 3) # 3 = max splits
517 file_path = split_output[0]
518 line_number = int(split_output[1])
519 col_number = int(split_output[2].split(' ')[0])
520 warning_message = split_output[3]
521 return file_path, line_number, col_number, warning_message
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800522
523
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800524def get_warn_patterns(platform):
525 """Get and initialize warn_patterns."""
526 warn_patterns = []
527 if platform == 'chrome':
528 warn_patterns = cpp_patterns.warn_patterns
529 elif platform == 'android':
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700530 warn_patterns = (make_patterns.warn_patterns + cpp_patterns.warn_patterns +
531 java_patterns.warn_patterns + tidy_patterns.warn_patterns +
532 other_patterns.warn_patterns)
Chih-Hung Hsieh888d1432019-12-09 19:32:03 -0800533 else:
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800534 raise Exception('platform name %s is not valid' % platform)
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700535 for pattern in warn_patterns:
536 pattern['members'] = []
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800537 # Each warning pattern has a 'projects' dictionary, that
538 # maps a project name to number of warnings in that project.
Chih-Hung Hsieha6068222021-04-30 14:30:58 -0700539 pattern['projects'] = {}
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800540 return warn_patterns
541
542
543def get_project_list(platform):
544 """Return project list for appropriate platform."""
545 if platform == 'chrome':
546 return chrome_project_list.project_list
547 if platform == 'android':
548 return android_project_list.project_list
549 raise Exception('platform name %s is not valid' % platform)
550
551
552def parallel_classify_warnings(warning_data, args, project_names,
553 project_patterns, warn_patterns,
554 use_google3, create_launch_subprocs_fn,
555 classify_warnings_fn):
556 """Classify all warning lines with num_cpu parallel processes."""
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700557 # pylint:disable=too-many-arguments,too-many-locals
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800558 num_cpu = args.processes
559 group_results = []
560
561 if num_cpu > 1:
562 # set up parallel processing for this...
563 warning_groups = [[] for _ in range(num_cpu)]
564 i = 0
565 for warning, link in warning_data.items():
566 warning_groups[i].append((warning, link))
567 i = (i + 1) % num_cpu
568 arg_groups = [[] for _ in range(num_cpu)]
569 for i, group in enumerate(warning_groups):
570 arg_groups[i] = [{
571 'group': group,
572 'project_patterns': project_patterns,
573 'warn_patterns': warn_patterns,
574 'num_processes': num_cpu
575 }]
576
577 group_results = create_launch_subprocs_fn(num_cpu,
578 classify_warnings_fn,
579 arg_groups,
580 group_results)
581 else:
582 group_results = []
583 for warning, link in warning_data.items():
584 classify_one_warning(warning, link, group_results,
585 project_patterns, warn_patterns)
586 group_results = [group_results]
587
588 warning_messages = []
589 warning_links = []
590 warning_records = []
591 if use_google3:
592 group_results = [group_results]
593 for group_result in group_results:
594 for result in group_result:
595 for line, link, pattern_idx, project_idx in result:
596 pattern = warn_patterns[pattern_idx]
597 pattern['members'].append(line)
598 message_idx = len(warning_messages)
599 warning_messages.append(line)
600 link_idx = len(warning_links)
601 warning_links.append(link)
602 warning_records.append([pattern_idx, project_idx, message_idx,
603 link_idx])
604 pname = '???' if project_idx < 0 else project_names[project_idx]
605 # Count warnings by project.
606 if pname in pattern['projects']:
607 pattern['projects'][pname] += 1
608 else:
609 pattern['projects'][pname] = 1
610 return warning_messages, warning_links, warning_records
611
612
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800613def process_log(logfile, flags, project_names, project_patterns, warn_patterns,
614 html_path, use_google3, create_launch_subprocs_fn,
615 classify_warnings_fn, logfile_object):
Chih-Hung Hsieh98b285d2021-04-28 14:49:32 -0700616 # pylint does not recognize g-doc-*
617 # pylint: disable=bad-option-value,g-doc-args
618 # pylint: disable=bad-option-value,g-doc-return-or-yield
619 # pylint: disable=too-many-arguments,too-many-locals
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800620 """Function that handles processing of a log.
621
622 This is isolated into its own function (rather than just taking place in main)
623 so that it can be used by both warn.py and the borg job process_gs_logs.py, to
624 avoid duplication of code.
625 Note that if the arguments to this function change, process_gs_logs.py must
626 be updated accordingly.
627 """
628 if logfile_object is None:
629 with io.open(logfile, encoding='utf-8') as log:
630 warning_lines_and_links, header_str = parse_input_file(log, flags)
631 else:
632 warning_lines_and_links, header_str = parse_input_file(
633 logfile_object, flags)
634 warning_messages, warning_links, warning_records = parallel_classify_warnings(
635 warning_lines_and_links, flags, project_names, project_patterns,
636 warn_patterns, use_google3, create_launch_subprocs_fn,
637 classify_warnings_fn)
638
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800639 html_writer.write_html(flags, project_names, warn_patterns, html_path,
640 warning_messages, warning_links, warning_records,
641 header_str)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800642
643 return warning_messages, warning_links, warning_records, header_str
644
645
646def common_main(use_google3, create_launch_subprocs_fn, classify_warnings_fn,
647 logfile_object=None):
648 """Shared main function for Google3 and non-Google3 versions of warn.py."""
649 flags = parse_args(use_google3)
650 warn_patterns = get_warn_patterns(flags.platform)
651 project_list = get_project_list(flags.platform)
652
653 project_names = get_project_names(project_list)
654 project_patterns = [re.compile(p[1]) for p in project_list]
655
656 # html_path=None because we output html below if not outputting CSV
657 warning_messages, warning_links, warning_records, header_str = process_log(
658 logfile=flags.log, flags=flags, project_names=project_names,
659 project_patterns=project_patterns, warn_patterns=warn_patterns,
660 html_path=None, use_google3=use_google3,
661 create_launch_subprocs_fn=create_launch_subprocs_fn,
662 classify_warnings_fn=classify_warnings_fn,
663 logfile_object=logfile_object)
664
Chih-Hung Hsieh3cce2bc2020-02-27 15:39:18 -0800665 html_writer.write_out_csv(flags, warn_patterns, warning_messages,
666 warning_links, warning_records, header_str,
667 project_names)
Chih-Hung Hsieh5ae55192020-02-24 10:20:36 -0800668
669 # Return these values, so that caller can use them, if desired.
670 return flags, warning_messages, warning_records, warn_patterns