| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging |
| 5 import os | 6 import os |
| 6 import re | 7 import re |
| 7 | 8 |
| 8 from waterfall import extractor_util | 9 from waterfall import extractor_util |
| 10 from waterfall import waterfall_config |
| 9 from waterfall.extractor import Extractor | 11 from waterfall.extractor import Extractor |
| 10 from waterfall.failure_signal import FailureSignal | 12 from waterfall.failure_signal import FailureSignal |
| 11 | 13 |
| 12 | 14 |
| 13 class GeneralExtractor(Extractor): | 15 class GeneralExtractor(Extractor): |
| 14 """A general extractor. | 16 """A general extractor. |
| 15 | 17 |
| 16 It extracts file name and line numbers. | 18 It extracts file name and line numbers. |
| 17 """ | 19 """ |
| 18 INDIRECT_LEAK_MARKER_PATTERN = re.compile( | 20 INDIRECT_LEAK_MARKER_PATTERN = re.compile( |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 WINDOWS_LINK_FAILURE_PATTERN = re.compile(r'/OUT:([^\s-]+)') | 124 WINDOWS_LINK_FAILURE_PATTERN = re.compile(r'/OUT:([^\s-]+)') |
| 123 | 125 |
| 124 NINJA_FAILURE_END_LINE_PREFIX = 'ninja: build stopped' | 126 NINJA_FAILURE_END_LINE_PREFIX = 'ninja: build stopped' |
| 125 NINJA_ERROR_LINE_PREFIX = 'ninja: error' | 127 NINJA_ERROR_LINE_PREFIX = 'ninja: error' |
| 126 ERROR_LINE_END_PATTERN = re.compile(r'^\d+ errors? generated.') | 128 ERROR_LINE_END_PATTERN = re.compile(r'^\d+ errors? generated.') |
| 127 IOS_ERROR_LINE_START_PREFIX = 'CompileC' | 129 IOS_ERROR_LINE_START_PREFIX = 'CompileC' |
| 128 | 130 |
| 129 IOS_BUILDER_NAMES_FOR_COMPILE = ['iOS_Simulator_(dbg)', 'iOS_Device'] | 131 IOS_BUILDER_NAMES_FOR_COMPILE = ['iOS_Simulator_(dbg)', 'iOS_Device'] |
| 130 MAC_MASTER_NAME_FOR_COMPILE = 'chromium.mac' | 132 MAC_MASTER_NAME_FOR_COMPILE = 'chromium.mac' |
| 131 | 133 |
| 134 GOMA_COMPILER_PREFIX = ( |
| 135 r'\S*/gomacc ' |
| 136 '\S+(?:clang\+\+|androideabi-gcc|androideabi-g\+\+)' |
| 137 ) |
| 138 STRICT_COMPILE_FAILURE_PATTEN = re.compile( |
| 139 r'^FAILED: %s -MMD -MF (obj/[^\s-]+\.o)\.d .* ' |
| 140 '-c ([^\s-]+(?:cc|c|cpp|m|mm)) -o \\1$' % ( |
| 141 GOMA_COMPILER_PREFIX)) |
| 142 |
| 143 STRICT_LINK_FAILURE_PATTEN = re.compile( |
| 144 r'^FAILED: %s -Wl,.* -o (\S+) -Wl,--start-group .*$' % ( |
| 145 GOMA_COMPILER_PREFIX)) |
| 146 |
| 147 def ExtractFailedOutputNodes(self, line, signal): |
| 148 match = self.STRICT_COMPILE_FAILURE_PATTEN.match(line) |
| 149 if match: |
| 150 target = match.group(1) |
| 151 source = match.group(2) |
| 152 signal.AddTarget({ |
| 153 'target': target, |
| 154 'source': source, |
| 155 }) |
| 156 else: |
| 157 match = self.STRICT_LINK_FAILURE_PATTEN.match(line) |
| 158 if match: |
| 159 target = match.group(1) |
| 160 signal.AddTarget({ |
| 161 'target': target, |
| 162 }) |
| 163 else: |
| 164 logging.warn('Unknown failure type: %s', line) |
| 165 |
| 132 def GetFailedTarget(self, line, signal): | 166 def GetFailedTarget(self, line, signal): |
| 133 match = self.LINUX_FAILED_SOURCE_TARGET_PATTERN.search(line) | 167 match = self.LINUX_FAILED_SOURCE_TARGET_PATTERN.search(line) |
| 134 | 168 |
| 135 if match: | 169 if match: |
| 136 # For non-windows builds, build lines should contain either gomacc or | 170 # For non-windows builds, build lines should contain either gomacc or |
| 137 # clang++, so only search for source/target files after the call to | 171 # clang++, so only search for source/target files after the call to |
| 138 # gomacc/clang++ to avoid extracting false positives in the failure line. | 172 # gomacc/clang++ to avoid extracting false positives in the failure line. |
| 139 build_line_match = self.LINUX_BUILD_COMMAND_PATTERN.search(line) | 173 build_line_match = self.LINUX_BUILD_COMMAND_PATTERN.search(line) |
| 140 if not build_line_match: | 174 if not build_line_match: |
| 141 return | 175 return |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 192 | 226 |
| 193 if failure_started: | 227 if failure_started: |
| 194 if line.startswith(self.IOS_ERROR_LINE_START_PREFIX): | 228 if line.startswith(self.IOS_ERROR_LINE_START_PREFIX): |
| 195 failure_started = False | 229 failure_started = False |
| 196 for l in error_lines[:-4]: | 230 for l in error_lines[:-4]: |
| 197 self.ExtractFiles(l, signal) | 231 self.ExtractFiles(l, signal) |
| 198 error_lines = [] | 232 error_lines = [] |
| 199 else: | 233 else: |
| 200 error_lines.append(line) | 234 error_lines.append(line) |
| 201 else: | 235 else: |
| 236 strict_regex = waterfall_config.EnableStrictRegexForCompileLinkFailures( |
| 237 master_name, bot_name) |
| 202 for line in failure_log.splitlines(): | 238 for line in failure_log.splitlines(): |
| 203 if line.startswith(self.FAILURE_START_LINE_PREFIX): | 239 if line.startswith(self.FAILURE_START_LINE_PREFIX): |
| 204 self.GetFailedTarget(line, signal) | 240 if strict_regex: |
| 241 self.ExtractFailedOutputNodes(line, signal) |
| 242 else: |
| 243 self.GetFailedTarget(line, signal) |
| 205 if not failure_started: | 244 if not failure_started: |
| 206 failure_started = True | 245 failure_started = True |
| 207 continue # pragma: no cover | 246 continue # pragma: no cover |
| 208 elif self.FAILURE_WITH_ERROR_PATTERN.match(line): | 247 elif self.FAILURE_WITH_ERROR_PATTERN.match(line): |
| 209 # It is possible the target and source file associated with a compile | 248 # It is possible the target and source file associated with a compile |
| 210 # failure is logged outside a 'FAILED: ... 1 error generated' block, | 249 # failure is logged outside a 'FAILED: ... 1 error generated' block, |
| 211 # so extract regardless of failure_started. | 250 # so extract regardless of failure_started. |
| 212 self.GetFailedTarget(line, signal) | 251 if not strict_regex: |
| 252 self.GetFailedTarget(line, signal) |
| 213 elif failure_started and self.ERROR_LINE_END_PATTERN.match(line): | 253 elif failure_started and self.ERROR_LINE_END_PATTERN.match(line): |
| 214 failure_started = False | 254 failure_started = False |
| 215 elif failure_started and line.startswith( | 255 elif failure_started and line.startswith( |
| 216 self.NINJA_FAILURE_END_LINE_PREFIX): # pragma: no cover | 256 self.NINJA_FAILURE_END_LINE_PREFIX): # pragma: no cover |
| 217 break | 257 break |
| 218 if failure_started or line.startswith(self.NINJA_ERROR_LINE_PREFIX): | 258 if failure_started or line.startswith(self.NINJA_ERROR_LINE_PREFIX): |
| 219 # either within the compile errors or is a ninja error. | 259 # either within the compile errors or is a ninja error. |
| 220 self.ExtractFiles(line, signal) | 260 self.ExtractFiles(line, signal) |
| 221 | 261 |
| 222 return signal | 262 return signal |
| (...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 474 def ExtractSignal(master_name, bot_name, step_name, test_name, failure_log): | 514 def ExtractSignal(master_name, bot_name, step_name, test_name, failure_log): |
| 475 """Uses an appropriate extractor to extract failure signals. | 515 """Uses an appropriate extractor to extract failure signals. |
| 476 | 516 |
| 477 Returns: | 517 Returns: |
| 478 A FailureSignal. | 518 A FailureSignal. |
| 479 """ | 519 """ |
| 480 # Fall back to a general-but-maybe-not-accurate extractor. | 520 # Fall back to a general-but-maybe-not-accurate extractor. |
| 481 extractor_class = EXTRACTORS.get(step_name, GeneralExtractor) | 521 extractor_class = EXTRACTORS.get(step_name, GeneralExtractor) |
| 482 return extractor_class().Extract( | 522 return extractor_class().Extract( |
| 483 failure_log, test_name, step_name, bot_name, master_name) | 523 failure_log, test_name, step_name, bot_name, master_name) |
| OLD | NEW |