Remove valgrind and include it using submodule
Change-Id: I4cfdb6bb908a386ad29b9932534cc5d187106f79
This commit is contained in:
parent
b387b4083c
commit
6046cde3d8
|
@ -23,3 +23,6 @@
|
||||||
[submodule "tools/gyp"]
|
[submodule "tools/gyp"]
|
||||||
path = tools/gyp
|
path = tools/gyp
|
||||||
url = https://chromium.googlesource.com/external/gyp.git
|
url = https://chromium.googlesource.com/external/gyp.git
|
||||||
|
[submodule "tools/valgrind"]
|
||||||
|
path = tools/valgrind
|
||||||
|
url = https://chromium.googlesource.com/chromium/src/tools/valgrind.git
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6cd50460a2a01992f4494955d9f345f1e6139db5
|
|
@ -1,5 +0,0 @@
|
||||||
set noparent
|
|
||||||
bruening@chromium.org
|
|
||||||
glider@chromium.org
|
|
||||||
thestig@chromium.org
|
|
||||||
timurrrr@chromium.org
|
|
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
from third_party import asan_symbolize
|
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def fix_filename(file_name):
|
|
||||||
for path_to_cut in sys.argv[1:]:
|
|
||||||
file_name = re.sub(".*" + path_to_cut, "", file_name)
|
|
||||||
file_name = re.sub(".*asan_[a-z_]*.cc:[0-9]*", "_asan_rtl_", file_name)
|
|
||||||
file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
|
|
||||||
return file_name
|
|
||||||
|
|
||||||
def main():
|
|
||||||
loop = asan_symbolize.SymbolizationLoop(binary_name_filter=fix_filename)
|
|
||||||
loop.process_stdin()
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# A wrapper that runs the program and filters the output through
|
|
||||||
# asan_symbolize.py and c++filt
|
|
||||||
#
|
|
||||||
# TODO(glider): this should be removed once EmbeddedTool in valgrind_test.py
|
|
||||||
# starts supporting pipes.
|
|
||||||
|
|
||||||
export THISDIR=`dirname $0`
|
|
||||||
"$@" 2>&1 |
|
|
||||||
$THISDIR/asan_symbolize.py |
|
|
||||||
c++filt
|
|
|
@ -1,6 +0,0 @@
|
||||||
Name: asan_symbolize.py
|
|
||||||
License: University of Illinois Open Source License.
|
|
||||||
Version: 183006
|
|
||||||
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/asan/scripts/asan_symbolize.py?view=co&content-type=text%2Fplain
|
|
||||||
|
|
||||||
asan_symbolize.py is a verbatim copy of asan_symbolize.py in the LLVM trunk.
|
|
|
@ -1,365 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
|
|
||||||
#
|
|
||||||
# The LLVM Compiler Infrastructure
|
|
||||||
#
|
|
||||||
# This file is distributed under the University of Illinois Open Source
|
|
||||||
# License. See LICENSE.TXT for details.
|
|
||||||
#
|
|
||||||
#===------------------------------------------------------------------------===#
|
|
||||||
import bisect
|
|
||||||
import getopt
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
llvm_symbolizer = None
|
|
||||||
symbolizers = {}
|
|
||||||
DEBUG = False
|
|
||||||
demangle = False;
|
|
||||||
|
|
||||||
|
|
||||||
# FIXME: merge the code that calls fix_filename().
|
|
||||||
def fix_filename(file_name):
|
|
||||||
for path_to_cut in sys.argv[1:]:
|
|
||||||
file_name = re.sub('.*' + path_to_cut, '', file_name)
|
|
||||||
file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
|
|
||||||
file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
|
|
||||||
return file_name
|
|
||||||
|
|
||||||
|
|
||||||
class Symbolizer(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
"""Symbolize the given address (pair of binary and offset).
|
|
||||||
|
|
||||||
Overriden in subclasses.
|
|
||||||
Args:
|
|
||||||
addr: virtual address of an instruction.
|
|
||||||
binary: path to executable/shared object containing this instruction.
|
|
||||||
offset: instruction offset in the @binary.
|
|
||||||
Returns:
|
|
||||||
list of strings (one string for each inlined frame) describing
|
|
||||||
the code locations for this instruction (that is, function name, file
|
|
||||||
name, line and column numbers).
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class LLVMSymbolizer(Symbolizer):
|
|
||||||
def __init__(self, symbolizer_path):
|
|
||||||
super(LLVMSymbolizer, self).__init__()
|
|
||||||
self.symbolizer_path = symbolizer_path
|
|
||||||
self.pipe = self.open_llvm_symbolizer()
|
|
||||||
|
|
||||||
def open_llvm_symbolizer(self):
|
|
||||||
if not os.path.exists(self.symbolizer_path):
|
|
||||||
return None
|
|
||||||
cmd = [self.symbolizer_path,
|
|
||||||
'--use-symbol-table=true',
|
|
||||||
'--demangle=%s' % demangle,
|
|
||||||
'--functions=true',
|
|
||||||
'--inlining=true']
|
|
||||||
if DEBUG:
|
|
||||||
print ' '.join(cmd)
|
|
||||||
return subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
"""Overrides Symbolizer.symbolize."""
|
|
||||||
if not self.pipe:
|
|
||||||
return None
|
|
||||||
result = []
|
|
||||||
try:
|
|
||||||
symbolizer_input = '%s %s' % (binary, offset)
|
|
||||||
if DEBUG:
|
|
||||||
print symbolizer_input
|
|
||||||
print >> self.pipe.stdin, symbolizer_input
|
|
||||||
while True:
|
|
||||||
function_name = self.pipe.stdout.readline().rstrip()
|
|
||||||
if not function_name:
|
|
||||||
break
|
|
||||||
file_name = self.pipe.stdout.readline().rstrip()
|
|
||||||
file_name = fix_filename(file_name)
|
|
||||||
if (not function_name.startswith('??') and
|
|
||||||
not file_name.startswith('??')):
|
|
||||||
# Append only valid frames.
|
|
||||||
result.append('%s in %s %s' % (addr, function_name,
|
|
||||||
file_name))
|
|
||||||
except Exception:
|
|
||||||
result = []
|
|
||||||
if not result:
|
|
||||||
result = None
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def LLVMSymbolizerFactory(system):
|
|
||||||
symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH')
|
|
||||||
if not symbolizer_path:
|
|
||||||
# Assume llvm-symbolizer is in PATH.
|
|
||||||
symbolizer_path = 'llvm-symbolizer'
|
|
||||||
return LLVMSymbolizer(symbolizer_path)
|
|
||||||
|
|
||||||
|
|
||||||
class Addr2LineSymbolizer(Symbolizer):
|
|
||||||
def __init__(self, binary):
|
|
||||||
super(Addr2LineSymbolizer, self).__init__()
|
|
||||||
self.binary = binary
|
|
||||||
self.pipe = self.open_addr2line()
|
|
||||||
|
|
||||||
def open_addr2line(self):
|
|
||||||
cmd = ['addr2line', '-f']
|
|
||||||
if demangle:
|
|
||||||
cmd += ['--demangle']
|
|
||||||
cmd += ['-e', self.binary]
|
|
||||||
if DEBUG:
|
|
||||||
print ' '.join(cmd)
|
|
||||||
return subprocess.Popen(cmd,
|
|
||||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
"""Overrides Symbolizer.symbolize."""
|
|
||||||
if self.binary != binary:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
print >> self.pipe.stdin, offset
|
|
||||||
function_name = self.pipe.stdout.readline().rstrip()
|
|
||||||
file_name = self.pipe.stdout.readline().rstrip()
|
|
||||||
except Exception:
|
|
||||||
function_name = ''
|
|
||||||
file_name = ''
|
|
||||||
file_name = fix_filename(file_name)
|
|
||||||
return ['%s in %s %s' % (addr, function_name, file_name)]
|
|
||||||
|
|
||||||
|
|
||||||
class DarwinSymbolizer(Symbolizer):
|
|
||||||
def __init__(self, addr, binary):
|
|
||||||
super(DarwinSymbolizer, self).__init__()
|
|
||||||
self.binary = binary
|
|
||||||
# Guess which arch we're running. 10 = len('0x') + 8 hex digits.
|
|
||||||
if len(addr) > 10:
|
|
||||||
self.arch = 'x86_64'
|
|
||||||
else:
|
|
||||||
self.arch = 'i386'
|
|
||||||
self.pipe = None
|
|
||||||
|
|
||||||
def write_addr_to_pipe(self, offset):
|
|
||||||
print >> self.pipe.stdin, '0x%x' % int(offset, 16)
|
|
||||||
|
|
||||||
def open_atos(self):
|
|
||||||
if DEBUG:
|
|
||||||
print 'atos -o %s -arch %s' % (self.binary, self.arch)
|
|
||||||
cmdline = ['atos', '-o', self.binary, '-arch', self.arch]
|
|
||||||
self.pipe = subprocess.Popen(cmdline,
|
|
||||||
stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
"""Overrides Symbolizer.symbolize."""
|
|
||||||
if self.binary != binary:
|
|
||||||
return None
|
|
||||||
self.open_atos()
|
|
||||||
self.write_addr_to_pipe(offset)
|
|
||||||
self.pipe.stdin.close()
|
|
||||||
atos_line = self.pipe.stdout.readline().rstrip()
|
|
||||||
# A well-formed atos response looks like this:
|
|
||||||
# foo(type1, type2) (in object.name) (filename.cc:80)
|
|
||||||
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
|
|
||||||
if DEBUG:
|
|
||||||
print 'atos_line: ', atos_line
|
|
||||||
if match:
|
|
||||||
function_name = match.group(1)
|
|
||||||
function_name = re.sub('\(.*?\)', '', function_name)
|
|
||||||
file_name = fix_filename(match.group(3))
|
|
||||||
return ['%s in %s %s' % (addr, function_name, file_name)]
|
|
||||||
else:
|
|
||||||
return ['%s in %s' % (addr, atos_line)]
|
|
||||||
|
|
||||||
|
|
||||||
# Chain several symbolizers so that if one symbolizer fails, we fall back
|
|
||||||
# to the next symbolizer in chain.
|
|
||||||
class ChainSymbolizer(Symbolizer):
|
|
||||||
def __init__(self, symbolizer_list):
|
|
||||||
super(ChainSymbolizer, self).__init__()
|
|
||||||
self.symbolizer_list = symbolizer_list
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
"""Overrides Symbolizer.symbolize."""
|
|
||||||
for symbolizer in self.symbolizer_list:
|
|
||||||
if symbolizer:
|
|
||||||
result = symbolizer.symbolize(addr, binary, offset)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
return None
|
|
||||||
|
|
||||||
def append_symbolizer(self, symbolizer):
|
|
||||||
self.symbolizer_list.append(symbolizer)
|
|
||||||
|
|
||||||
|
|
||||||
def BreakpadSymbolizerFactory(binary):
|
|
||||||
suffix = os.getenv('BREAKPAD_SUFFIX')
|
|
||||||
if suffix:
|
|
||||||
filename = binary + suffix
|
|
||||||
if os.access(filename, os.F_OK):
|
|
||||||
return BreakpadSymbolizer(filename)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def SystemSymbolizerFactory(system, addr, binary):
|
|
||||||
if system == 'Darwin':
|
|
||||||
return DarwinSymbolizer(addr, binary)
|
|
||||||
elif system == 'Linux':
|
|
||||||
return Addr2LineSymbolizer(binary)
|
|
||||||
|
|
||||||
|
|
||||||
class BreakpadSymbolizer(Symbolizer):
|
|
||||||
def __init__(self, filename):
|
|
||||||
super(BreakpadSymbolizer, self).__init__()
|
|
||||||
self.filename = filename
|
|
||||||
lines = file(filename).readlines()
|
|
||||||
self.files = []
|
|
||||||
self.symbols = {}
|
|
||||||
self.address_list = []
|
|
||||||
self.addresses = {}
|
|
||||||
# MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t
|
|
||||||
fragments = lines[0].rstrip().split()
|
|
||||||
self.arch = fragments[2]
|
|
||||||
self.debug_id = fragments[3]
|
|
||||||
self.binary = ' '.join(fragments[4:])
|
|
||||||
self.parse_lines(lines[1:])
|
|
||||||
|
|
||||||
def parse_lines(self, lines):
|
|
||||||
cur_function_addr = ''
|
|
||||||
for line in lines:
|
|
||||||
fragments = line.split()
|
|
||||||
if fragments[0] == 'FILE':
|
|
||||||
assert int(fragments[1]) == len(self.files)
|
|
||||||
self.files.append(' '.join(fragments[2:]))
|
|
||||||
elif fragments[0] == 'PUBLIC':
|
|
||||||
self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:])
|
|
||||||
elif fragments[0] in ['CFI', 'STACK']:
|
|
||||||
pass
|
|
||||||
elif fragments[0] == 'FUNC':
|
|
||||||
cur_function_addr = int(fragments[1], 16)
|
|
||||||
if not cur_function_addr in self.symbols.keys():
|
|
||||||
self.symbols[cur_function_addr] = ' '.join(fragments[4:])
|
|
||||||
else:
|
|
||||||
# Line starting with an address.
|
|
||||||
addr = int(fragments[0], 16)
|
|
||||||
self.address_list.append(addr)
|
|
||||||
# Tuple of symbol address, size, line, file number.
|
|
||||||
self.addresses[addr] = (cur_function_addr,
|
|
||||||
int(fragments[1], 16),
|
|
||||||
int(fragments[2]),
|
|
||||||
int(fragments[3]))
|
|
||||||
self.address_list.sort()
|
|
||||||
|
|
||||||
def get_sym_file_line(self, addr):
|
|
||||||
key = None
|
|
||||||
if addr in self.addresses.keys():
|
|
||||||
key = addr
|
|
||||||
else:
|
|
||||||
index = bisect.bisect_left(self.address_list, addr)
|
|
||||||
if index == 0:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
key = self.address_list[index - 1]
|
|
||||||
sym_id, size, line_no, file_no = self.addresses[key]
|
|
||||||
symbol = self.symbols[sym_id]
|
|
||||||
filename = self.files[file_no]
|
|
||||||
if addr < key + size:
|
|
||||||
return symbol, filename, line_no
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def symbolize(self, addr, binary, offset):
|
|
||||||
if self.binary != binary:
|
|
||||||
return None
|
|
||||||
res = self.get_sym_file_line(int(offset, 16))
|
|
||||||
if res:
|
|
||||||
function_name, file_name, line_no = res
|
|
||||||
result = ['%s in %s %s:%d' % (
|
|
||||||
addr, function_name, file_name, line_no)]
|
|
||||||
print result
|
|
||||||
return result
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class SymbolizationLoop(object):
|
|
||||||
def __init__(self, binary_name_filter=None):
|
|
||||||
# Used by clients who may want to supply a different binary name.
|
|
||||||
# E.g. in Chrome several binaries may share a single .dSYM.
|
|
||||||
self.binary_name_filter = binary_name_filter
|
|
||||||
self.system = os.uname()[0]
|
|
||||||
if self.system in ['Linux', 'Darwin']:
|
|
||||||
self.llvm_symbolizer = LLVMSymbolizerFactory(self.system)
|
|
||||||
else:
|
|
||||||
raise Exception('Unknown system')
|
|
||||||
|
|
||||||
def symbolize_address(self, addr, binary, offset):
|
|
||||||
# Use the chain of symbolizers:
|
|
||||||
# Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos
|
|
||||||
# (fall back to next symbolizer if the previous one fails).
|
|
||||||
if not binary in symbolizers:
|
|
||||||
symbolizers[binary] = ChainSymbolizer(
|
|
||||||
[BreakpadSymbolizerFactory(binary), self.llvm_symbolizer])
|
|
||||||
result = symbolizers[binary].symbolize(addr, binary, offset)
|
|
||||||
if result is None:
|
|
||||||
# Initialize system symbolizer only if other symbolizers failed.
|
|
||||||
symbolizers[binary].append_symbolizer(
|
|
||||||
SystemSymbolizerFactory(self.system, addr, binary))
|
|
||||||
result = symbolizers[binary].symbolize(addr, binary, offset)
|
|
||||||
# The system symbolizer must produce some result.
|
|
||||||
assert result
|
|
||||||
return result
|
|
||||||
|
|
||||||
def print_symbolized_lines(self, symbolized_lines):
|
|
||||||
if not symbolized_lines:
|
|
||||||
print self.current_line
|
|
||||||
else:
|
|
||||||
for symbolized_frame in symbolized_lines:
|
|
||||||
print ' #' + str(self.frame_no) + ' ' + symbolized_frame.rstrip()
|
|
||||||
self.frame_no += 1
|
|
||||||
|
|
||||||
def process_stdin(self):
|
|
||||||
self.frame_no = 0
|
|
||||||
while True:
|
|
||||||
line = sys.stdin.readline()
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
self.current_line = line.rstrip()
|
|
||||||
#0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
|
|
||||||
stack_trace_line_format = (
|
|
||||||
'^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
|
|
||||||
match = re.match(stack_trace_line_format, line)
|
|
||||||
if not match:
|
|
||||||
print self.current_line
|
|
||||||
continue
|
|
||||||
if DEBUG:
|
|
||||||
print line
|
|
||||||
_, frameno_str, addr, binary, offset = match.groups()
|
|
||||||
if frameno_str == '0':
|
|
||||||
# Assume that frame #0 is the first frame of new stack trace.
|
|
||||||
self.frame_no = 0
|
|
||||||
original_binary = binary
|
|
||||||
if self.binary_name_filter:
|
|
||||||
binary = self.binary_name_filter(binary)
|
|
||||||
symbolized_line = self.symbolize_address(addr, binary, offset)
|
|
||||||
if not symbolized_line:
|
|
||||||
if original_binary != binary:
|
|
||||||
symbolized_line = self.symbolize_address(addr, binary, offset)
|
|
||||||
self.print_symbolized_lines(symbolized_line)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
opts, args = getopt.getopt(sys.argv[1:], "d", ["demangle"])
|
|
||||||
for o, a in opts:
|
|
||||||
if o in ("-d", "--demangle"):
|
|
||||||
demangle = True;
|
|
||||||
loop = SymbolizationLoop()
|
|
||||||
loop.process_stdin()
|
|
|
@ -1,49 +0,0 @@
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
# TODO(timurrrr): we may use it on POSIX too to avoid code duplication once we
|
|
||||||
# support layout_tests, remove Dr. Memory specific code and verify it works
|
|
||||||
# on a "clean" Mac.
|
|
||||||
|
|
||||||
testcase_name = None
|
|
||||||
for arg in sys.argv:
|
|
||||||
m = re.match("\-\-test\-name=(.*)", arg)
|
|
||||||
if m:
|
|
||||||
assert testcase_name is None
|
|
||||||
testcase_name = m.groups()[0]
|
|
||||||
|
|
||||||
# arg #0 is the path to this python script
|
|
||||||
cmd_to_run = sys.argv[1:]
|
|
||||||
|
|
||||||
# TODO(timurrrr): this is Dr. Memory-specific
|
|
||||||
# Usually, we pass "-logdir" "foo\bar\spam path" args to Dr. Memory.
|
|
||||||
# To group reports per UI test, we want to put the reports for each test into a
|
|
||||||
# separate directory. This code can be simplified when we have
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=684 fixed.
|
|
||||||
logdir_idx = cmd_to_run.index("-logdir")
|
|
||||||
old_logdir = cmd_to_run[logdir_idx + 1]
|
|
||||||
|
|
||||||
wrapper_pid = str(os.getpid())
|
|
||||||
|
|
||||||
# On Windows, there is a chance of PID collision. We avoid it by appending the
|
|
||||||
# number of entries in the logdir at the end of wrapper_pid.
|
|
||||||
# This number is monotonic and we can't have two simultaneously running wrappers
|
|
||||||
# with the same PID.
|
|
||||||
wrapper_pid += "_%d" % len(glob.glob(old_logdir + "\\*"))
|
|
||||||
|
|
||||||
cmd_to_run[logdir_idx + 1] += "\\testcase.%s.logs" % wrapper_pid
|
|
||||||
os.makedirs(cmd_to_run[logdir_idx + 1])
|
|
||||||
|
|
||||||
if testcase_name:
|
|
||||||
f = open(old_logdir + "\\testcase.%s.name" % wrapper_pid, "w")
|
|
||||||
print >>f, testcase_name
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
exit(subprocess.call(cmd_to_run))
|
|
|
@ -1,70 +0,0 @@
|
||||||
@echo off
|
|
||||||
:: Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
:: Use of this source code is governed by a BSD-style license that can be
|
|
||||||
:: found in the LICENSE file.
|
|
||||||
|
|
||||||
:: TODO(timurrrr): batch files 'export' all the variables to the parent shell
|
|
||||||
set THISDIR=%~dp0
|
|
||||||
set TOOL_NAME="unknown"
|
|
||||||
|
|
||||||
:: Get the tool name and put it into TOOL_NAME {{{1
|
|
||||||
:: NB: SHIFT command doesn't modify %*
|
|
||||||
:PARSE_ARGS_LOOP
|
|
||||||
if %1 == () GOTO:TOOLNAME_NOT_FOUND
|
|
||||||
if %1 == --tool GOTO:TOOLNAME_FOUND
|
|
||||||
SHIFT
|
|
||||||
goto :PARSE_ARGS_LOOP
|
|
||||||
|
|
||||||
:TOOLNAME_NOT_FOUND
|
|
||||||
echo "Please specify a tool (tsan or drmemory) by using --tool flag"
|
|
||||||
exit /B 1
|
|
||||||
|
|
||||||
:TOOLNAME_FOUND
|
|
||||||
SHIFT
|
|
||||||
set TOOL_NAME=%1
|
|
||||||
:: }}}
|
|
||||||
if "%TOOL_NAME%" == "drmemory" GOTO :SETUP_DRMEMORY
|
|
||||||
if "%TOOL_NAME%" == "drmemory_light" GOTO :SETUP_DRMEMORY
|
|
||||||
if "%TOOL_NAME%" == "drmemory_full" GOTO :SETUP_DRMEMORY
|
|
||||||
if "%TOOL_NAME%" == "drmemory_pattern" GOTO :SETUP_DRMEMORY
|
|
||||||
if "%TOOL_NAME%" == "tsan" GOTO :SETUP_TSAN
|
|
||||||
echo "Unknown tool: `%TOOL_NAME%`! Only tsan and drmemory are supported right now"
|
|
||||||
exit /B 1
|
|
||||||
|
|
||||||
:SETUP_DRMEMORY
|
|
||||||
if NOT "%DRMEMORY_COMMAND%"=="" GOTO :RUN_TESTS
|
|
||||||
:: Set up DRMEMORY_COMMAND to invoke Dr. Memory {{{1
|
|
||||||
set DRMEMORY_PATH=%THISDIR%..\..\third_party\drmemory
|
|
||||||
set DRMEMORY_SFX=%DRMEMORY_PATH%\drmemory-windows-sfx.exe
|
|
||||||
if EXIST %DRMEMORY_SFX% GOTO DRMEMORY_BINARY_OK
|
|
||||||
echo "Can't find Dr. Memory executables."
|
|
||||||
echo "See http://www.chromium.org/developers/how-tos/using-valgrind/dr-memory"
|
|
||||||
echo "for the instructions on how to get them."
|
|
||||||
exit /B 1
|
|
||||||
|
|
||||||
:DRMEMORY_BINARY_OK
|
|
||||||
%DRMEMORY_SFX% -o%DRMEMORY_PATH%\unpacked -y
|
|
||||||
set DRMEMORY_COMMAND=%DRMEMORY_PATH%\unpacked\bin\drmemory.exe
|
|
||||||
:: }}}
|
|
||||||
goto :RUN_TESTS
|
|
||||||
|
|
||||||
:SETUP_TSAN
|
|
||||||
:: Set up PIN_COMMAND to invoke TSan {{{1
|
|
||||||
set TSAN_PATH=%THISDIR%..\..\third_party\tsan
|
|
||||||
set TSAN_SFX=%TSAN_PATH%\tsan-x86-windows-sfx.exe
|
|
||||||
if EXIST %TSAN_SFX% GOTO TSAN_BINARY_OK
|
|
||||||
echo "Can't find ThreadSanitizer executables."
|
|
||||||
echo "See http://www.chromium.org/developers/how-tos/using-valgrind/threadsanitizer/threadsanitizer-on-windows"
|
|
||||||
echo "for the instructions on how to get them."
|
|
||||||
exit /B 1
|
|
||||||
|
|
||||||
:TSAN_BINARY_OK
|
|
||||||
%TSAN_SFX% -o%TSAN_PATH%\unpacked -y
|
|
||||||
set PIN_COMMAND=%TSAN_PATH%\unpacked\tsan-x86-windows\tsan.bat
|
|
||||||
:: }}}
|
|
||||||
goto :RUN_TESTS
|
|
||||||
|
|
||||||
:RUN_TESTS
|
|
||||||
set PYTHONPATH=%THISDIR%../python/google
|
|
||||||
set RUNNING_ON_VALGRIND=yes
|
|
||||||
python %THISDIR%/chrome_tests.py %*
|
|
|
@ -1,626 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
''' Runs various chrome tests through valgrind_test.py.'''
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import logging
|
|
||||||
import multiprocessing
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import stat
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import logging_utils
|
|
||||||
import path_utils
|
|
||||||
|
|
||||||
import common
|
|
||||||
import valgrind_test
|
|
||||||
|
|
||||||
class TestNotFound(Exception): pass
|
|
||||||
|
|
||||||
class MultipleGTestFiltersSpecified(Exception): pass
|
|
||||||
|
|
||||||
class BuildDirNotFound(Exception): pass
|
|
||||||
|
|
||||||
class BuildDirAmbiguous(Exception): pass
|
|
||||||
|
|
||||||
class ChromeTests:
|
|
||||||
SLOW_TOOLS = ["memcheck", "tsan", "tsan_rv", "drmemory"]
|
|
||||||
LAYOUT_TESTS_DEFAULT_CHUNK_SIZE = 500
|
|
||||||
|
|
||||||
def __init__(self, options, args, test):
|
|
||||||
if ':' in test:
|
|
||||||
(self._test, self._gtest_filter) = test.split(':', 1)
|
|
||||||
else:
|
|
||||||
self._test = test
|
|
||||||
self._gtest_filter = options.gtest_filter
|
|
||||||
|
|
||||||
if self._test not in self._test_list:
|
|
||||||
raise TestNotFound("Unknown test: %s" % test)
|
|
||||||
|
|
||||||
if options.gtest_filter and options.gtest_filter != self._gtest_filter:
|
|
||||||
raise MultipleGTestFiltersSpecified("Can not specify both --gtest_filter "
|
|
||||||
"and --test %s" % test)
|
|
||||||
|
|
||||||
self._options = options
|
|
||||||
self._args = args
|
|
||||||
|
|
||||||
script_dir = path_utils.ScriptDir()
|
|
||||||
# Compute the top of the tree (the "source dir") from the script dir (where
|
|
||||||
# this script lives). We assume that the script dir is in tools/valgrind/
|
|
||||||
# relative to the top of the tree.
|
|
||||||
self._source_dir = os.path.dirname(os.path.dirname(script_dir))
|
|
||||||
# since this path is used for string matching, make sure it's always
|
|
||||||
# an absolute Unix-style path
|
|
||||||
self._source_dir = os.path.abspath(self._source_dir).replace('\\', '/')
|
|
||||||
valgrind_test_script = os.path.join(script_dir, "valgrind_test.py")
|
|
||||||
self._command_preamble = ["--source_dir=%s" % (self._source_dir)]
|
|
||||||
|
|
||||||
if not self._options.build_dir:
|
|
||||||
dirs = [
|
|
||||||
os.path.join(self._source_dir, "xcodebuild", "Debug"),
|
|
||||||
os.path.join(self._source_dir, "out", "Debug"),
|
|
||||||
os.path.join(self._source_dir, "build", "Debug"),
|
|
||||||
]
|
|
||||||
build_dir = [d for d in dirs if os.path.isdir(d)]
|
|
||||||
if len(build_dir) > 1:
|
|
||||||
raise BuildDirAmbiguous("Found more than one suitable build dir:\n"
|
|
||||||
"%s\nPlease specify just one "
|
|
||||||
"using --build_dir" % ", ".join(build_dir))
|
|
||||||
elif build_dir:
|
|
||||||
self._options.build_dir = build_dir[0]
|
|
||||||
else:
|
|
||||||
self._options.build_dir = None
|
|
||||||
|
|
||||||
if self._options.build_dir:
|
|
||||||
build_dir = os.path.abspath(self._options.build_dir)
|
|
||||||
self._command_preamble += ["--build_dir=%s" % (self._options.build_dir)]
|
|
||||||
|
|
||||||
def _EnsureBuildDirFound(self):
|
|
||||||
if not self._options.build_dir:
|
|
||||||
raise BuildDirNotFound("Oops, couldn't find a build dir, please "
|
|
||||||
"specify it manually using --build_dir")
|
|
||||||
|
|
||||||
def _DefaultCommand(self, tool, exe=None, valgrind_test_args=None):
|
|
||||||
'''Generates the default command array that most tests will use.'''
|
|
||||||
if exe and common.IsWindows():
|
|
||||||
exe += '.exe'
|
|
||||||
|
|
||||||
cmd = list(self._command_preamble)
|
|
||||||
|
|
||||||
# Find all suppressions matching the following pattern:
|
|
||||||
# tools/valgrind/TOOL/suppressions[_PLATFORM].txt
|
|
||||||
# and list them with --suppressions= prefix.
|
|
||||||
script_dir = path_utils.ScriptDir()
|
|
||||||
tool_name = tool.ToolName();
|
|
||||||
suppression_file = os.path.join(script_dir, tool_name, "suppressions.txt")
|
|
||||||
if os.path.exists(suppression_file):
|
|
||||||
cmd.append("--suppressions=%s" % suppression_file)
|
|
||||||
# Platform-specific suppression
|
|
||||||
for platform in common.PlatformNames():
|
|
||||||
platform_suppression_file = \
|
|
||||||
os.path.join(script_dir, tool_name, 'suppressions_%s.txt' % platform)
|
|
||||||
if os.path.exists(platform_suppression_file):
|
|
||||||
cmd.append("--suppressions=%s" % platform_suppression_file)
|
|
||||||
|
|
||||||
if self._options.valgrind_tool_flags:
|
|
||||||
cmd += self._options.valgrind_tool_flags.split(" ")
|
|
||||||
if self._options.keep_logs:
|
|
||||||
cmd += ["--keep_logs"]
|
|
||||||
if valgrind_test_args != None:
|
|
||||||
for arg in valgrind_test_args:
|
|
||||||
cmd.append(arg)
|
|
||||||
if exe:
|
|
||||||
self._EnsureBuildDirFound()
|
|
||||||
cmd.append(os.path.join(self._options.build_dir, exe))
|
|
||||||
# Valgrind runs tests slowly, so slow tests hurt more; show elapased time
|
|
||||||
# so we can find the slowpokes.
|
|
||||||
cmd.append("--gtest_print_time")
|
|
||||||
if self._options.gtest_repeat:
|
|
||||||
cmd.append("--gtest_repeat=%s" % self._options.gtest_repeat)
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def Run(self):
|
|
||||||
''' Runs the test specified by command-line argument --test '''
|
|
||||||
logging.info("running test %s" % (self._test))
|
|
||||||
return self._test_list[self._test](self)
|
|
||||||
|
|
||||||
def _AppendGtestFilter(self, tool, name, cmd):
|
|
||||||
'''Append an appropriate --gtest_filter flag to the googletest binary
|
|
||||||
invocation.
|
|
||||||
If the user passed his own filter mentioning only one test, just use it.
|
|
||||||
Othewise, filter out tests listed in the appropriate gtest_exclude files.
|
|
||||||
'''
|
|
||||||
if (self._gtest_filter and
|
|
||||||
":" not in self._gtest_filter and
|
|
||||||
"?" not in self._gtest_filter and
|
|
||||||
"*" not in self._gtest_filter):
|
|
||||||
cmd.append("--gtest_filter=%s" % self._gtest_filter)
|
|
||||||
return
|
|
||||||
|
|
||||||
filters = []
|
|
||||||
gtest_files_dir = os.path.join(path_utils.ScriptDir(), "gtest_exclude")
|
|
||||||
|
|
||||||
gtest_filter_files = [
|
|
||||||
os.path.join(gtest_files_dir, name + ".gtest-%s.txt" % tool.ToolName())]
|
|
||||||
# Use ".gtest.txt" files only for slow tools, as they now contain
|
|
||||||
# Valgrind- and Dr.Memory-specific filters.
|
|
||||||
# TODO(glider): rename the files to ".gtest_slow.txt"
|
|
||||||
if tool.ToolName() in ChromeTests.SLOW_TOOLS:
|
|
||||||
gtest_filter_files += [os.path.join(gtest_files_dir, name + ".gtest.txt")]
|
|
||||||
for platform_suffix in common.PlatformNames():
|
|
||||||
gtest_filter_files += [
|
|
||||||
os.path.join(gtest_files_dir, name + ".gtest_%s.txt" % platform_suffix),
|
|
||||||
os.path.join(gtest_files_dir, name + ".gtest-%s_%s.txt" % \
|
|
||||||
(tool.ToolName(), platform_suffix))]
|
|
||||||
logging.info("Reading gtest exclude filter files:")
|
|
||||||
for filename in gtest_filter_files:
|
|
||||||
# strip the leading absolute path (may be very long on the bot)
|
|
||||||
# and the following / or \.
|
|
||||||
readable_filename = filename.replace("\\", "/") # '\' on Windows
|
|
||||||
readable_filename = readable_filename.replace(self._source_dir, "")[1:]
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
logging.info(" \"%s\" - not found" % readable_filename)
|
|
||||||
continue
|
|
||||||
logging.info(" \"%s\" - OK" % readable_filename)
|
|
||||||
f = open(filename, 'r')
|
|
||||||
for line in f.readlines():
|
|
||||||
if line.startswith("#") or line.startswith("//") or line.isspace():
|
|
||||||
continue
|
|
||||||
line = line.rstrip()
|
|
||||||
test_prefixes = ["FLAKY", "FAILS"]
|
|
||||||
for p in test_prefixes:
|
|
||||||
# Strip prefixes from the test names.
|
|
||||||
line = line.replace(".%s_" % p, ".")
|
|
||||||
# Exclude the original test name.
|
|
||||||
filters.append(line)
|
|
||||||
if line[-2:] != ".*":
|
|
||||||
# List all possible prefixes if line doesn't end with ".*".
|
|
||||||
for p in test_prefixes:
|
|
||||||
filters.append(line.replace(".", ".%s_" % p))
|
|
||||||
# Get rid of duplicates.
|
|
||||||
filters = set(filters)
|
|
||||||
gtest_filter = self._gtest_filter
|
|
||||||
if len(filters):
|
|
||||||
if gtest_filter:
|
|
||||||
gtest_filter += ":"
|
|
||||||
if gtest_filter.find("-") < 0:
|
|
||||||
gtest_filter += "-"
|
|
||||||
else:
|
|
||||||
gtest_filter = "-"
|
|
||||||
gtest_filter += ":".join(filters)
|
|
||||||
if gtest_filter:
|
|
||||||
cmd.append("--gtest_filter=%s" % gtest_filter)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def ShowTests():
|
|
||||||
test_to_names = {}
|
|
||||||
for name, test_function in ChromeTests._test_list.iteritems():
|
|
||||||
test_to_names.setdefault(test_function, []).append(name)
|
|
||||||
|
|
||||||
name_to_aliases = {}
|
|
||||||
for names in test_to_names.itervalues():
|
|
||||||
names.sort(key=lambda name: len(name))
|
|
||||||
name_to_aliases[names[0]] = names[1:]
|
|
||||||
|
|
||||||
print
|
|
||||||
print "Available tests:"
|
|
||||||
print "----------------"
|
|
||||||
for name, aliases in sorted(name_to_aliases.iteritems()):
|
|
||||||
if aliases:
|
|
||||||
print " {} (aka {})".format(name, ', '.join(aliases))
|
|
||||||
else:
|
|
||||||
print " {}".format(name)
|
|
||||||
|
|
||||||
def SetupLdPath(self, requires_build_dir):
|
|
||||||
if requires_build_dir:
|
|
||||||
self._EnsureBuildDirFound()
|
|
||||||
elif not self._options.build_dir:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Append build_dir to LD_LIBRARY_PATH so external libraries can be loaded.
|
|
||||||
if (os.getenv("LD_LIBRARY_PATH")):
|
|
||||||
os.putenv("LD_LIBRARY_PATH", "%s:%s" % (os.getenv("LD_LIBRARY_PATH"),
|
|
||||||
self._options.build_dir))
|
|
||||||
else:
|
|
||||||
os.putenv("LD_LIBRARY_PATH", self._options.build_dir)
|
|
||||||
|
|
||||||
def SimpleTest(self, module, name, valgrind_test_args=None, cmd_args=None):
|
|
||||||
tool = valgrind_test.CreateTool(self._options.valgrind_tool)
|
|
||||||
cmd = self._DefaultCommand(tool, name, valgrind_test_args)
|
|
||||||
self._AppendGtestFilter(tool, name, cmd)
|
|
||||||
cmd.extend(['--test-tiny-timeout=1000'])
|
|
||||||
if cmd_args:
|
|
||||||
cmd.extend(cmd_args)
|
|
||||||
|
|
||||||
self.SetupLdPath(True)
|
|
||||||
return tool.Run(cmd, module)
|
|
||||||
|
|
||||||
def RunCmdLine(self):
|
|
||||||
tool = valgrind_test.CreateTool(self._options.valgrind_tool)
|
|
||||||
cmd = self._DefaultCommand(tool, None, self._args)
|
|
||||||
self.SetupLdPath(False)
|
|
||||||
return tool.Run(cmd, None)
|
|
||||||
|
|
||||||
def TestAppList(self):
|
|
||||||
return self.SimpleTest("app_list", "app_list_unittests")
|
|
||||||
|
|
||||||
def TestAsh(self):
|
|
||||||
return self.SimpleTest("ash", "ash_unittests")
|
|
||||||
|
|
||||||
def TestAura(self):
|
|
||||||
return self.SimpleTest("aura", "aura_unittests")
|
|
||||||
|
|
||||||
def TestBase(self):
|
|
||||||
return self.SimpleTest("base", "base_unittests")
|
|
||||||
|
|
||||||
def TestChromeOS(self):
|
|
||||||
return self.SimpleTest("chromeos", "chromeos_unittests")
|
|
||||||
|
|
||||||
def TestComponents(self):
|
|
||||||
return self.SimpleTest("components", "components_unittests")
|
|
||||||
|
|
||||||
def TestCompositor(self):
|
|
||||||
return self.SimpleTest("compositor", "compositor_unittests")
|
|
||||||
|
|
||||||
def TestContent(self):
|
|
||||||
return self.SimpleTest("content", "content_unittests")
|
|
||||||
|
|
||||||
def TestContentBrowser(self):
|
|
||||||
return self.SimpleTest("content", "content_browsertests")
|
|
||||||
|
|
||||||
def TestCourgette(self):
|
|
||||||
return self.SimpleTest("courgette", "courgette_unittests")
|
|
||||||
|
|
||||||
def TestCrypto(self):
|
|
||||||
return self.SimpleTest("crypto", "crypto_unittests")
|
|
||||||
|
|
||||||
def TestDevice(self):
|
|
||||||
return self.SimpleTest("device", "device_unittests")
|
|
||||||
|
|
||||||
def TestFFmpeg(self):
|
|
||||||
return self.SimpleTest("chrome", "ffmpeg_unittests")
|
|
||||||
|
|
||||||
def TestFFmpegRegressions(self):
|
|
||||||
return self.SimpleTest("chrome", "ffmpeg_regression_tests")
|
|
||||||
|
|
||||||
def TestGPU(self):
|
|
||||||
return self.SimpleTest("gpu", "gpu_unittests")
|
|
||||||
|
|
||||||
def TestIpc(self):
|
|
||||||
return self.SimpleTest("ipc", "ipc_tests",
|
|
||||||
valgrind_test_args=["--trace_children"])
|
|
||||||
|
|
||||||
def TestJingle(self):
|
|
||||||
return self.SimpleTest("chrome", "jingle_unittests")
|
|
||||||
|
|
||||||
def TestMedia(self):
|
|
||||||
return self.SimpleTest("chrome", "media_unittests")
|
|
||||||
|
|
||||||
def TestMessageCenter(self):
|
|
||||||
return self.SimpleTest("message_center", "message_center_unittests")
|
|
||||||
|
|
||||||
def TestNet(self):
|
|
||||||
return self.SimpleTest("net", "net_unittests")
|
|
||||||
|
|
||||||
def TestNetPerf(self):
|
|
||||||
return self.SimpleTest("net", "net_perftests")
|
|
||||||
|
|
||||||
def TestPPAPI(self):
|
|
||||||
return self.SimpleTest("chrome", "ppapi_unittests")
|
|
||||||
|
|
||||||
def TestPrinting(self):
|
|
||||||
return self.SimpleTest("chrome", "printing_unittests")
|
|
||||||
|
|
||||||
def TestRemoting(self):
|
|
||||||
return self.SimpleTest("chrome", "remoting_unittests",
|
|
||||||
cmd_args=[
|
|
||||||
"--ui-test-action-timeout=60000",
|
|
||||||
"--ui-test-action-max-timeout=150000"])
|
|
||||||
|
|
||||||
def TestSql(self):
|
|
||||||
return self.SimpleTest("chrome", "sql_unittests")
|
|
||||||
|
|
||||||
def TestSync(self):
|
|
||||||
return self.SimpleTest("chrome", "sync_unit_tests")
|
|
||||||
|
|
||||||
def TestLinuxSandbox(self):
|
|
||||||
return self.SimpleTest("sandbox", "sandbox_linux_unittests")
|
|
||||||
|
|
||||||
def TestUnit(self):
|
|
||||||
# http://crbug.com/51716
|
|
||||||
# Disabling all unit tests
|
|
||||||
# Problems reappeared after r119922
|
|
||||||
if common.IsMac() and (self._options.valgrind_tool == "memcheck"):
|
|
||||||
logging.warning("unit_tests are disabled for memcheck on MacOS.")
|
|
||||||
return 0;
|
|
||||||
return self.SimpleTest("chrome", "unit_tests")
|
|
||||||
|
|
||||||
def TestUIUnit(self):
|
|
||||||
return self.SimpleTest("chrome", "ui_unittests")
|
|
||||||
|
|
||||||
def TestURL(self):
|
|
||||||
return self.SimpleTest("chrome", "url_unittests")
|
|
||||||
|
|
||||||
def TestViews(self):
|
|
||||||
return self.SimpleTest("views", "views_unittests")
|
|
||||||
|
|
||||||
# Valgrind timeouts are in seconds.
|
|
||||||
UI_VALGRIND_ARGS = ["--timeout=14400", "--trace_children", "--indirect"]
|
|
||||||
# UI test timeouts are in milliseconds.
|
|
||||||
UI_TEST_ARGS = ["--ui-test-action-timeout=60000",
|
|
||||||
"--ui-test-action-max-timeout=150000",
|
|
||||||
"--no-sandbox"]
|
|
||||||
|
|
||||||
# TODO(thestig) fine-tune these values.
|
|
||||||
# Valgrind timeouts are in seconds.
|
|
||||||
BROWSER_VALGRIND_ARGS = ["--timeout=50000", "--trace_children", "--indirect"]
|
|
||||||
# Browser test timeouts are in milliseconds.
|
|
||||||
BROWSER_TEST_ARGS = ["--ui-test-action-timeout=400000",
|
|
||||||
"--ui-test-action-max-timeout=800000",
|
|
||||||
"--no-sandbox"]
|
|
||||||
|
|
||||||
def TestAutomatedUI(self):
|
|
||||||
return self.SimpleTest("chrome", "automated_ui_tests",
|
|
||||||
valgrind_test_args=self.UI_VALGRIND_ARGS,
|
|
||||||
cmd_args=self.UI_TEST_ARGS)
|
|
||||||
|
|
||||||
def TestBrowser(self):
|
|
||||||
return self.SimpleTest("chrome", "browser_tests",
|
|
||||||
valgrind_test_args=self.BROWSER_VALGRIND_ARGS,
|
|
||||||
cmd_args=self.BROWSER_TEST_ARGS)
|
|
||||||
|
|
||||||
def TestInteractiveUI(self):
|
|
||||||
return self.SimpleTest("chrome", "interactive_ui_tests",
|
|
||||||
valgrind_test_args=self.UI_VALGRIND_ARGS,
|
|
||||||
cmd_args=self.UI_TEST_ARGS)
|
|
||||||
|
|
||||||
def TestReliability(self):
|
|
||||||
script_dir = path_utils.ScriptDir()
|
|
||||||
url_list_file = os.path.join(script_dir, "reliability", "url_list.txt")
|
|
||||||
return self.SimpleTest("chrome", "reliability_tests",
|
|
||||||
valgrind_test_args=self.UI_VALGRIND_ARGS,
|
|
||||||
cmd_args=(self.UI_TEST_ARGS +
|
|
||||||
["--list=%s" % url_list_file]))
|
|
||||||
|
|
||||||
def TestSafeBrowsing(self):
|
|
||||||
return self.SimpleTest("chrome", "safe_browsing_tests",
|
|
||||||
valgrind_test_args=self.UI_VALGRIND_ARGS,
|
|
||||||
cmd_args=(["--ui-test-action-max-timeout=450000"]))
|
|
||||||
|
|
||||||
def TestSyncIntegration(self):
|
|
||||||
return self.SimpleTest("chrome", "sync_integration_tests",
|
|
||||||
valgrind_test_args=self.UI_VALGRIND_ARGS,
|
|
||||||
cmd_args=(["--ui-test-action-max-timeout=450000"]))
|
|
||||||
|
|
||||||
def TestLayoutChunk(self, chunk_num, chunk_size):
|
|
||||||
# Run tests [chunk_num*chunk_size .. (chunk_num+1)*chunk_size) from the
|
|
||||||
# list of tests. Wrap around to beginning of list at end.
|
|
||||||
# If chunk_size is zero, run all tests in the list once.
|
|
||||||
# If a text file is given as argument, it is used as the list of tests.
|
|
||||||
#
|
|
||||||
# Build the ginormous commandline in 'cmd'.
|
|
||||||
# It's going to be roughly
|
|
||||||
# python valgrind_test.py ... python run_webkit_tests.py ...
|
|
||||||
# but we'll use the --indirect flag to valgrind_test.py
|
|
||||||
# to avoid valgrinding python.
|
|
||||||
# Start by building the valgrind_test.py commandline.
|
|
||||||
tool = valgrind_test.CreateTool(self._options.valgrind_tool)
|
|
||||||
cmd = self._DefaultCommand(tool)
|
|
||||||
cmd.append("--trace_children")
|
|
||||||
cmd.append("--indirect_webkit_layout")
|
|
||||||
cmd.append("--ignore_exit_code")
|
|
||||||
# Now build script_cmd, the run_webkits_tests.py commandline
|
|
||||||
# Store each chunk in its own directory so that we can find the data later
|
|
||||||
chunk_dir = os.path.join("layout", "chunk_%05d" % chunk_num)
|
|
||||||
out_dir = os.path.join(path_utils.ScriptDir(), "latest")
|
|
||||||
out_dir = os.path.join(out_dir, chunk_dir)
|
|
||||||
if os.path.exists(out_dir):
|
|
||||||
old_files = glob.glob(os.path.join(out_dir, "*.txt"))
|
|
||||||
for f in old_files:
|
|
||||||
os.remove(f)
|
|
||||||
else:
|
|
||||||
os.makedirs(out_dir)
|
|
||||||
script = os.path.join(self._source_dir, "webkit", "tools", "layout_tests",
|
|
||||||
"run_webkit_tests.py")
|
|
||||||
# http://crbug.com/260627: After the switch to content_shell from DRT, each
|
|
||||||
# test now brings up 3 processes. Under Valgrind, they become memory bound
|
|
||||||
# and can eventually OOM if we don't reduce the total count.
|
|
||||||
jobs = int(multiprocessing.cpu_count() * 0.3)
|
|
||||||
script_cmd = ["python", script, "-v",
|
|
||||||
"--run-singly", # run a separate DumpRenderTree for each test
|
|
||||||
"--fully-parallel",
|
|
||||||
"--child-processes=%d" % jobs,
|
|
||||||
"--time-out-ms=200000",
|
|
||||||
"--no-retry-failures", # retrying takes too much time
|
|
||||||
# http://crbug.com/176908: Don't launch a browser when done.
|
|
||||||
"--no-show-results",
|
|
||||||
"--nocheck-sys-deps"]
|
|
||||||
# Pass build mode to run_webkit_tests.py. We aren't passed it directly,
|
|
||||||
# so parse it out of build_dir. run_webkit_tests.py can only handle
|
|
||||||
# the two values "Release" and "Debug".
|
|
||||||
# TODO(Hercules): unify how all our scripts pass around build mode
|
|
||||||
# (--mode / --target / --build_dir / --debug)
|
|
||||||
if self._options.build_dir.endswith("Debug"):
|
|
||||||
script_cmd.append("--debug");
|
|
||||||
if (chunk_size > 0):
|
|
||||||
script_cmd.append("--run-chunk=%d:%d" % (chunk_num, chunk_size))
|
|
||||||
if len(self._args):
|
|
||||||
# if the arg is a txt file, then treat it as a list of tests
|
|
||||||
if os.path.isfile(self._args[0]) and self._args[0][-4:] == ".txt":
|
|
||||||
script_cmd.append("--test-list=%s" % self._args[0])
|
|
||||||
else:
|
|
||||||
script_cmd.extend(self._args)
|
|
||||||
self._AppendGtestFilter(tool, "layout", script_cmd)
|
|
||||||
# Now run script_cmd with the wrapper in cmd
|
|
||||||
cmd.extend(["--"])
|
|
||||||
cmd.extend(script_cmd)
|
|
||||||
|
|
||||||
# Layout tests often times fail quickly, but the buildbot remains green.
|
|
||||||
# Detect this situation when running with the default chunk size.
|
|
||||||
if chunk_size == self.LAYOUT_TESTS_DEFAULT_CHUNK_SIZE:
|
|
||||||
min_runtime_in_seconds=120
|
|
||||||
else:
|
|
||||||
min_runtime_in_seconds=0
|
|
||||||
ret = tool.Run(cmd, "layout", min_runtime_in_seconds=min_runtime_in_seconds)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def TestLayout(self):
|
|
||||||
# A "chunk file" is maintained in the local directory so that each test
|
|
||||||
# runs a slice of the layout tests of size chunk_size that increments with
|
|
||||||
# each run. Since tests can be added and removed from the layout tests at
|
|
||||||
# any time, this is not going to give exact coverage, but it will allow us
|
|
||||||
# to continuously run small slices of the layout tests under valgrind rather
|
|
||||||
# than having to run all of them in one shot.
|
|
||||||
chunk_size = self._options.num_tests
|
|
||||||
if (chunk_size == 0):
|
|
||||||
return self.TestLayoutChunk(0, 0)
|
|
||||||
chunk_num = 0
|
|
||||||
chunk_file = os.path.join("valgrind_layout_chunk.txt")
|
|
||||||
logging.info("Reading state from " + chunk_file)
|
|
||||||
try:
|
|
||||||
f = open(chunk_file)
|
|
||||||
if f:
|
|
||||||
str = f.read()
|
|
||||||
if len(str):
|
|
||||||
chunk_num = int(str)
|
|
||||||
# This should be enough so that we have a couple of complete runs
|
|
||||||
# of test data stored in the archive (although note that when we loop
|
|
||||||
# that we almost guaranteed won't be at the end of the test list)
|
|
||||||
if chunk_num > 10000:
|
|
||||||
chunk_num = 0
|
|
||||||
f.close()
|
|
||||||
except IOError, (errno, strerror):
|
|
||||||
logging.error("error reading from file %s (%d, %s)" % (chunk_file,
|
|
||||||
errno, strerror))
|
|
||||||
# Save the new chunk size before running the tests. Otherwise if a
|
|
||||||
# particular chunk hangs the bot, the chunk number will never get
|
|
||||||
# incremented and the bot will be wedged.
|
|
||||||
logging.info("Saving state to " + chunk_file)
|
|
||||||
try:
|
|
||||||
f = open(chunk_file, "w")
|
|
||||||
chunk_num += 1
|
|
||||||
f.write("%d" % chunk_num)
|
|
||||||
f.close()
|
|
||||||
except IOError, (errno, strerror):
|
|
||||||
logging.error("error writing to file %s (%d, %s)" % (chunk_file, errno,
|
|
||||||
strerror))
|
|
||||||
# Since we're running small chunks of the layout tests, it's important to
|
|
||||||
# mark the ones that have errors in them. These won't be visible in the
|
|
||||||
# summary list for long, but will be useful for someone reviewing this bot.
|
|
||||||
return self.TestLayoutChunk(chunk_num, chunk_size)
|
|
||||||
|
|
||||||
# The known list of tests.
|
|
||||||
# Recognise the original abbreviations as well as full executable names.
|
|
||||||
_test_list = {
|
|
||||||
"cmdline" : RunCmdLine,
|
|
||||||
"app_list": TestAppList, "app_list_unittests": TestAppList,
|
|
||||||
"ash": TestAsh, "ash_unittests": TestAsh,
|
|
||||||
"aura": TestAura, "aura_unittests": TestAura,
|
|
||||||
"automated_ui" : TestAutomatedUI,
|
|
||||||
"base": TestBase, "base_unittests": TestBase,
|
|
||||||
"browser": TestBrowser, "browser_tests": TestBrowser,
|
|
||||||
"chromeos": TestChromeOS, "chromeos_unittests": TestChromeOS,
|
|
||||||
"components": TestComponents,"components_unittests": TestComponents,
|
|
||||||
"compositor": TestCompositor,"compositor_unittests": TestCompositor,
|
|
||||||
"content": TestContent, "content_unittests": TestContent,
|
|
||||||
"content_browsertests": TestContentBrowser,
|
|
||||||
"courgette": TestCourgette, "courgette_unittests": TestCourgette,
|
|
||||||
"crypto": TestCrypto, "crypto_unittests": TestCrypto,
|
|
||||||
"device": TestDevice, "device_unittests": TestDevice,
|
|
||||||
"ffmpeg": TestFFmpeg, "ffmpeg_unittests": TestFFmpeg,
|
|
||||||
"ffmpeg_regression_tests": TestFFmpegRegressions,
|
|
||||||
"gpu": TestGPU, "gpu_unittests": TestGPU,
|
|
||||||
"ipc": TestIpc, "ipc_tests": TestIpc,
|
|
||||||
"interactive_ui": TestInteractiveUI,
|
|
||||||
"jingle": TestJingle, "jingle_unittests": TestJingle,
|
|
||||||
"layout": TestLayout, "layout_tests": TestLayout,
|
|
||||||
"webkit": TestLayout,
|
|
||||||
"media": TestMedia, "media_unittests": TestMedia,
|
|
||||||
"message_center": TestMessageCenter,
|
|
||||||
"message_center_unittests" : TestMessageCenter,
|
|
||||||
"net": TestNet, "net_unittests": TestNet,
|
|
||||||
"net_perf": TestNetPerf, "net_perftests": TestNetPerf,
|
|
||||||
"ppapi": TestPPAPI, "ppapi_unittests": TestPPAPI,
|
|
||||||
"printing": TestPrinting, "printing_unittests": TestPrinting,
|
|
||||||
"reliability": TestReliability, "reliability_tests": TestReliability,
|
|
||||||
"remoting": TestRemoting, "remoting_unittests": TestRemoting,
|
|
||||||
"safe_browsing": TestSafeBrowsing, "safe_browsing_tests": TestSafeBrowsing,
|
|
||||||
"sandbox": TestLinuxSandbox, "sandbox_linux_unittests": TestLinuxSandbox,
|
|
||||||
"sql": TestSql, "sql_unittests": TestSql,
|
|
||||||
"sync": TestSync, "sync_unit_tests": TestSync,
|
|
||||||
"sync_integration_tests": TestSyncIntegration,
|
|
||||||
"sync_integration": TestSyncIntegration,
|
|
||||||
"ui_unit": TestUIUnit, "ui_unittests": TestUIUnit,
|
|
||||||
"unit": TestUnit, "unit_tests": TestUnit,
|
|
||||||
"url": TestURL, "url_unittests": TestURL,
|
|
||||||
"views": TestViews, "views_unittests": TestViews,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
|
||||||
parser = optparse.OptionParser("usage: %prog -b <dir> -t <test> "
|
|
||||||
"[-t <test> ...]")
|
|
||||||
parser.disable_interspersed_args()
|
|
||||||
|
|
||||||
parser.add_option("", "--help-tests", dest="help_tests", action="store_true",
|
|
||||||
default=False, help="List all available tests")
|
|
||||||
parser.add_option("-b", "--build_dir",
|
|
||||||
help="the location of the compiler output")
|
|
||||||
parser.add_option("-t", "--test", action="append", default=[],
|
|
||||||
help="which test to run, supports test:gtest_filter format "
|
|
||||||
"as well.")
|
|
||||||
parser.add_option("", "--baseline", action="store_true", default=False,
|
|
||||||
help="generate baseline data instead of validating")
|
|
||||||
parser.add_option("", "--gtest_filter",
|
|
||||||
help="additional arguments to --gtest_filter")
|
|
||||||
parser.add_option("", "--gtest_repeat",
|
|
||||||
help="argument for --gtest_repeat")
|
|
||||||
parser.add_option("-v", "--verbose", action="store_true", default=False,
|
|
||||||
help="verbose output - enable debug log messages")
|
|
||||||
parser.add_option("", "--tool", dest="valgrind_tool", default="memcheck",
|
|
||||||
help="specify a valgrind tool to run the tests under")
|
|
||||||
parser.add_option("", "--tool_flags", dest="valgrind_tool_flags", default="",
|
|
||||||
help="specify custom flags for the selected valgrind tool")
|
|
||||||
parser.add_option("", "--keep_logs", action="store_true", default=False,
|
|
||||||
help="store memory tool logs in the <tool>.logs directory "
|
|
||||||
"instead of /tmp.\nThis can be useful for tool "
|
|
||||||
"developers/maintainers.\nPlease note that the <tool>"
|
|
||||||
".logs directory will be clobbered on tool startup.")
|
|
||||||
parser.add_option("-n", "--num_tests", type="int",
|
|
||||||
default=ChromeTests.LAYOUT_TESTS_DEFAULT_CHUNK_SIZE,
|
|
||||||
help="for layout tests: # of subtests per run. 0 for all.")
|
|
||||||
# TODO(thestig) Remove this if we can.
|
|
||||||
parser.add_option("", "--gtest_color", dest="gtest_color", default="no",
|
|
||||||
help="dummy compatibility flag for sharding_supervisor.")
|
|
||||||
|
|
||||||
options, args = parser.parse_args()
|
|
||||||
|
|
||||||
if options.verbose:
|
|
||||||
logging_utils.config_root(logging.DEBUG)
|
|
||||||
else:
|
|
||||||
logging_utils.config_root()
|
|
||||||
|
|
||||||
if options.help_tests:
|
|
||||||
ChromeTests.ShowTests()
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if not options.test:
|
|
||||||
parser.error("--test not specified")
|
|
||||||
|
|
||||||
if len(options.test) != 1 and options.gtest_filter:
|
|
||||||
parser.error("--gtest_filter and multiple tests don't make sense together")
|
|
||||||
|
|
||||||
for t in options.test:
|
|
||||||
tests = ChromeTests(options, args, t)
|
|
||||||
ret = tests.Run()
|
|
||||||
if ret: return ret
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(_main())
|
|
|
@ -1,122 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# Set up some paths and re-direct the arguments to chrome_tests.py
|
|
||||||
|
|
||||||
export THISDIR=`dirname $0`
|
|
||||||
ARGV_COPY="$@"
|
|
||||||
|
|
||||||
# We need to set CHROME_VALGRIND iff using Memcheck or TSan-Valgrind:
|
|
||||||
# tools/valgrind/chrome_tests.sh --tool memcheck
|
|
||||||
# or
|
|
||||||
# tools/valgrind/chrome_tests.sh --tool=memcheck
|
|
||||||
# (same for "--tool=tsan")
|
|
||||||
tool="memcheck" # Default to memcheck.
|
|
||||||
while (( "$#" ))
|
|
||||||
do
|
|
||||||
if [[ "$1" == "--tool" ]]
|
|
||||||
then
|
|
||||||
tool="$2"
|
|
||||||
shift
|
|
||||||
elif [[ "$1" =~ --tool=(.*) ]]
|
|
||||||
then
|
|
||||||
tool="${BASH_REMATCH[1]}"
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
NEEDS_VALGRIND=0
|
|
||||||
NEEDS_DRMEMORY=0
|
|
||||||
|
|
||||||
case "$tool" in
|
|
||||||
"memcheck")
|
|
||||||
NEEDS_VALGRIND=1
|
|
||||||
;;
|
|
||||||
"tsan" | "tsan_rv")
|
|
||||||
if [ "`uname -s`" == CYGWIN* ]
|
|
||||||
then
|
|
||||||
NEEDS_PIN=1
|
|
||||||
else
|
|
||||||
NEEDS_VALGRIND=1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"drmemory" | "drmemory_light" | "drmemory_full" | "drmemory_pattern")
|
|
||||||
NEEDS_DRMEMORY=1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ "$NEEDS_VALGRIND" == "1" ]
|
|
||||||
then
|
|
||||||
CHROME_VALGRIND=`sh $THISDIR/locate_valgrind.sh`
|
|
||||||
if [ "$CHROME_VALGRIND" = "" ]
|
|
||||||
then
|
|
||||||
# locate_valgrind.sh failed
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Using valgrind binaries from ${CHROME_VALGRIND}"
|
|
||||||
|
|
||||||
PATH="${CHROME_VALGRIND}/bin:$PATH"
|
|
||||||
# We need to set these variables to override default lib paths hard-coded into
|
|
||||||
# Valgrind binary.
|
|
||||||
export VALGRIND_LIB="$CHROME_VALGRIND/lib/valgrind"
|
|
||||||
export VALGRIND_LIB_INNER="$CHROME_VALGRIND/lib/valgrind"
|
|
||||||
|
|
||||||
# Clean up some /tmp directories that might be stale due to interrupted
|
|
||||||
# chrome_tests.py execution.
|
|
||||||
# FYI:
|
|
||||||
# -mtime +1 <- only print files modified more than 24h ago,
|
|
||||||
# -print0/-0 are needed to handle possible newlines in the filenames.
|
|
||||||
echo "Cleanup /tmp from Valgrind stuff"
|
|
||||||
find /tmp -maxdepth 1 \(\
|
|
||||||
-name "vgdb-pipe-*" -or -name "vg_logs_*" -or -name "valgrind.*" \
|
|
||||||
\) -mtime +1 -print0 | xargs -0 rm -rf
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$NEEDS_DRMEMORY" == "1" ]
|
|
||||||
then
|
|
||||||
if [ -z "$DRMEMORY_COMMAND" ]
|
|
||||||
then
|
|
||||||
DRMEMORY_PATH="$THISDIR/../../third_party/drmemory"
|
|
||||||
DRMEMORY_SFX="$DRMEMORY_PATH/drmemory-windows-sfx.exe"
|
|
||||||
if [ ! -f "$DRMEMORY_SFX" ]
|
|
||||||
then
|
|
||||||
echo "Can't find Dr. Memory executables."
|
|
||||||
echo "See http://www.chromium.org/developers/how-tos/using-valgrind/dr-memory"
|
|
||||||
echo "for the instructions on how to get them."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x "$DRMEMORY_SFX" # Cygwin won't run it without +x.
|
|
||||||
"$DRMEMORY_SFX" -o"$DRMEMORY_PATH/unpacked" -y
|
|
||||||
export DRMEMORY_COMMAND="$DRMEMORY_PATH/unpacked/bin/drmemory.exe"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$NEEDS_PIN" == "1" ]
|
|
||||||
then
|
|
||||||
if [ -z "$PIN_COMMAND" ]
|
|
||||||
then
|
|
||||||
# Set up PIN_COMMAND to invoke TSan.
|
|
||||||
TSAN_PATH="$THISDIR/../../third_party/tsan"
|
|
||||||
TSAN_SFX="$TSAN_PATH/tsan-x86-windows-sfx.exe"
|
|
||||||
echo "$TSAN_SFX"
|
|
||||||
if [ ! -f $TSAN_SFX ]
|
|
||||||
then
|
|
||||||
echo "Can't find ThreadSanitizer executables."
|
|
||||||
echo "See http://www.chromium.org/developers/how-tos/using-valgrind/threadsanitizer/threadsanitizer-on-windows"
|
|
||||||
echo "for the instructions on how to get them."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x "$TSAN_SFX" # Cygwin won't run it without +x.
|
|
||||||
"$TSAN_SFX" -o"$TSAN_PATH"/unpacked -y
|
|
||||||
export PIN_COMMAND="$TSAN_PATH/unpacked/tsan-x86-windows/tsan.bat"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
PYTHONPATH=$THISDIR/../python/google python \
|
|
||||||
"$THISDIR/chrome_tests.py" $ARGV_COPY
|
|
|
@ -1,252 +0,0 @@
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import platform
|
|
||||||
import os
|
|
||||||
import signal
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class NotImplementedError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TimeoutError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def RunSubprocessInBackground(proc):
|
|
||||||
"""Runs a subprocess in the background. Returns a handle to the process."""
|
|
||||||
logging.info("running %s in the background" % " ".join(proc))
|
|
||||||
return subprocess.Popen(proc)
|
|
||||||
|
|
||||||
|
|
||||||
def RunSubprocess(proc, timeout=0):
|
|
||||||
""" Runs a subprocess, until it finishes or |timeout| is exceeded and the
|
|
||||||
process is killed with taskkill. A |timeout| <= 0 means no timeout.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
proc: list of process components (exe + args)
|
|
||||||
timeout: how long to wait before killing, <= 0 means wait forever
|
|
||||||
"""
|
|
||||||
|
|
||||||
logging.info("running %s, timeout %d sec" % (" ".join(proc), timeout))
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.flush()
|
|
||||||
|
|
||||||
# Manually read and print out stdout and stderr.
|
|
||||||
# By default, the subprocess is supposed to inherit these from its parent,
|
|
||||||
# however when run under buildbot, it seems unable to read data from a
|
|
||||||
# grandchild process, so we have to read the child and print the data as if
|
|
||||||
# it came from us for buildbot to read it. We're not sure why this is
|
|
||||||
# necessary.
|
|
||||||
# TODO(erikkay): should we buffer stderr and stdout separately?
|
|
||||||
p = subprocess.Popen(proc, universal_newlines=True,
|
|
||||||
bufsize=0, # unbuffered
|
|
||||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
|
||||||
|
|
||||||
logging.info("started subprocess")
|
|
||||||
|
|
||||||
did_timeout = False
|
|
||||||
if timeout > 0:
|
|
||||||
wait_until = time.time() + timeout
|
|
||||||
while p.poll() is None and not did_timeout:
|
|
||||||
# Have to use readline rather than readlines() or "for line in p.stdout:",
|
|
||||||
# otherwise we get buffered even with bufsize=0.
|
|
||||||
line = p.stdout.readline()
|
|
||||||
while line and not did_timeout:
|
|
||||||
sys.stdout.write(line)
|
|
||||||
sys.stdout.flush()
|
|
||||||
line = p.stdout.readline()
|
|
||||||
if timeout > 0:
|
|
||||||
did_timeout = time.time() > wait_until
|
|
||||||
|
|
||||||
if did_timeout:
|
|
||||||
logging.info("process timed out")
|
|
||||||
else:
|
|
||||||
logging.info("process ended, did not time out")
|
|
||||||
|
|
||||||
if did_timeout:
|
|
||||||
if IsWindows():
|
|
||||||
subprocess.call(["taskkill", "/T", "/F", "/PID", str(p.pid)])
|
|
||||||
else:
|
|
||||||
# Does this kill all children, too?
|
|
||||||
os.kill(p.pid, signal.SIGINT)
|
|
||||||
logging.error("KILLED %d" % p.pid)
|
|
||||||
# Give the process a chance to actually die before continuing
|
|
||||||
# so that cleanup can happen safely.
|
|
||||||
time.sleep(1.0)
|
|
||||||
logging.error("TIMEOUT waiting for %s" % proc[0])
|
|
||||||
raise TimeoutError(proc[0])
|
|
||||||
else:
|
|
||||||
for line in p.stdout:
|
|
||||||
sys.stdout.write(line)
|
|
||||||
if not IsMac(): # stdout flush fails on Mac
|
|
||||||
logging.info("flushing stdout")
|
|
||||||
sys.stdout.flush()
|
|
||||||
|
|
||||||
logging.info("collecting result code")
|
|
||||||
result = p.poll()
|
|
||||||
if result:
|
|
||||||
logging.error("%s exited with non-zero result code %d" % (proc[0], result))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def IsLinux():
|
|
||||||
return sys.platform.startswith('linux')
|
|
||||||
|
|
||||||
|
|
||||||
def IsMac():
|
|
||||||
return sys.platform.startswith('darwin')
|
|
||||||
|
|
||||||
|
|
||||||
def IsWindows():
|
|
||||||
return sys.platform == 'cygwin' or sys.platform.startswith('win')
|
|
||||||
|
|
||||||
|
|
||||||
def WindowsVersionName():
|
|
||||||
"""Returns the name of the Windows version if it is known, or None.
|
|
||||||
|
|
||||||
Possible return values are: xp, vista, 7, 8, or None
|
|
||||||
"""
|
|
||||||
if sys.platform == 'cygwin':
|
|
||||||
# Windows version number is hiding in system name. Looks like:
|
|
||||||
# CYGWIN_NT-6.1-WOW64
|
|
||||||
try:
|
|
||||||
version_str = platform.uname()[0].split('-')[1]
|
|
||||||
except:
|
|
||||||
return None
|
|
||||||
elif sys.platform.startswith('win'):
|
|
||||||
# Normal Windows version string. Mine: 6.1.7601
|
|
||||||
version_str = platform.version()
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
parts = version_str.split('.')
|
|
||||||
try:
|
|
||||||
major = int(parts[0])
|
|
||||||
minor = int(parts[1])
|
|
||||||
except:
|
|
||||||
return None # Can't parse, unknown version.
|
|
||||||
|
|
||||||
if major == 5:
|
|
||||||
return 'xp'
|
|
||||||
elif major == 6 and minor == 0:
|
|
||||||
return 'vista'
|
|
||||||
elif major == 6 and minor == 1:
|
|
||||||
return '7'
|
|
||||||
elif major == 6 and minor == 2:
|
|
||||||
return '8' # Future proof. ;)
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def PlatformNames():
|
|
||||||
"""Return an array of string to be used in paths for the platform
|
|
||||||
(e.g. suppressions, gtest filters, ignore files etc.)
|
|
||||||
The first element of the array describes the 'main' platform
|
|
||||||
"""
|
|
||||||
if IsLinux():
|
|
||||||
return ['linux']
|
|
||||||
if IsMac():
|
|
||||||
return ['mac']
|
|
||||||
if IsWindows():
|
|
||||||
names = ['win32']
|
|
||||||
version_name = WindowsVersionName()
|
|
||||||
if version_name is not None:
|
|
||||||
names.append('win-%s' % version_name)
|
|
||||||
return names
|
|
||||||
raise NotImplementedError('Unknown platform "%s".' % sys.platform)
|
|
||||||
|
|
||||||
|
|
||||||
def PutEnvAndLog(env_name, env_value):
|
|
||||||
os.putenv(env_name, env_value)
|
|
||||||
logging.info('export %s=%s', env_name, env_value)
|
|
||||||
|
|
||||||
def BoringCallers(mangled, use_re_wildcards):
|
|
||||||
"""Return a list of 'boring' function names (optinally mangled)
|
|
||||||
with */? wildcards (optionally .*/.).
|
|
||||||
Boring = we drop off the bottom of stack traces below such functions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
need_mangling = [
|
|
||||||
# Don't show our testing framework:
|
|
||||||
("testing::Test::Run", "_ZN7testing4Test3RunEv"),
|
|
||||||
("testing::TestInfo::Run", "_ZN7testing8TestInfo3RunEv"),
|
|
||||||
("testing::internal::Handle*ExceptionsInMethodIfSupported*",
|
|
||||||
"_ZN7testing8internal3?Handle*ExceptionsInMethodIfSupported*"),
|
|
||||||
|
|
||||||
# Depend on scheduling:
|
|
||||||
("MessageLoop::Run", "_ZN11MessageLoop3RunEv"),
|
|
||||||
("MessageLoop::RunTask", "_ZN11MessageLoop7RunTask*"),
|
|
||||||
("RunnableMethod*", "_ZN14RunnableMethod*"),
|
|
||||||
("DispatchToMethod*", "_Z*16DispatchToMethod*"),
|
|
||||||
("base::internal::Invoker*::DoInvoke*",
|
|
||||||
"_ZN4base8internal8Invoker*DoInvoke*"), # Invoker{1,2,3}
|
|
||||||
("base::internal::RunnableAdapter*::Run*",
|
|
||||||
"_ZN4base8internal15RunnableAdapter*Run*"),
|
|
||||||
]
|
|
||||||
|
|
||||||
ret = []
|
|
||||||
for pair in need_mangling:
|
|
||||||
ret.append(pair[1 if mangled else 0])
|
|
||||||
|
|
||||||
ret += [
|
|
||||||
# Also don't show the internals of libc/pthread.
|
|
||||||
"start_thread",
|
|
||||||
"main",
|
|
||||||
"BaseThreadInitThunk",
|
|
||||||
]
|
|
||||||
|
|
||||||
if use_re_wildcards:
|
|
||||||
for i in range(0, len(ret)):
|
|
||||||
ret[i] = ret[i].replace('*', '.*').replace('?', '.')
|
|
||||||
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def NormalizeWindowsPath(path):
|
|
||||||
"""If we're using Cygwin Python, turn the path into a Windows path.
|
|
||||||
|
|
||||||
Don't turn forward slashes into backslashes for easier copy-pasting and
|
|
||||||
escaping.
|
|
||||||
|
|
||||||
TODO(rnk): If we ever want to cut out the subprocess invocation, we can use
|
|
||||||
_winreg to get the root Cygwin directory from the registry key:
|
|
||||||
HKEY_LOCAL_MACHINE\SOFTWARE\Cygwin\setup\rootdir.
|
|
||||||
"""
|
|
||||||
if sys.platform.startswith("cygwin"):
|
|
||||||
p = subprocess.Popen(["cygpath", "-m", path],
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE)
|
|
||||||
(out, err) = p.communicate()
|
|
||||||
if err:
|
|
||||||
logging.warning("WARNING: cygpath error: %s", err)
|
|
||||||
return out.strip()
|
|
||||||
else:
|
|
||||||
return path
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Common output format code
|
|
||||||
|
|
||||||
def PrintUsedSuppressionsList(suppcounts):
|
|
||||||
""" Prints out the list of used suppressions in a format common to all the
|
|
||||||
memory tools. If the list is empty, prints nothing and returns False,
|
|
||||||
otherwise True.
|
|
||||||
|
|
||||||
suppcounts: a dictionary of used suppression counts,
|
|
||||||
Key -> name, Value -> count.
|
|
||||||
"""
|
|
||||||
if not suppcounts:
|
|
||||||
return False
|
|
||||||
|
|
||||||
print "-----------------------------------------------------"
|
|
||||||
print "Suppressions used:"
|
|
||||||
print " count name"
|
|
||||||
for (name, count) in sorted(suppcounts.items(), key=lambda (k,v): (v,k)):
|
|
||||||
print "%7d %s" % (count, name)
|
|
||||||
print "-----------------------------------------------------"
|
|
||||||
sys.stdout.flush()
|
|
||||||
return True
|
|
|
@ -1,5 +0,0 @@
|
||||||
@echo off
|
|
||||||
:: Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
:: Use of this source code is governed by a BSD-style license that can be
|
|
||||||
:: found in the LICENSE file.
|
|
||||||
%~dp0\chrome_tests.bat -t cmdline --tool drmemory %*
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -1,35 +0,0 @@
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
"""
|
|
||||||
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
|
|
||||||
for more details on the presubmit API built into gcl.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChange(input_api, output_api):
|
|
||||||
"""Checks the DrMemory suppression files for bad suppressions."""
|
|
||||||
|
|
||||||
# TODO(timurrrr): find out how to do relative imports
|
|
||||||
# and remove this ugly hack. Also, the CheckChange function won't be needed.
|
|
||||||
tools_vg_path = input_api.os_path.join(input_api.PresubmitLocalPath(), '..')
|
|
||||||
import sys
|
|
||||||
old_path = sys.path
|
|
||||||
try:
|
|
||||||
sys.path = sys.path + [tools_vg_path]
|
|
||||||
import suppressions
|
|
||||||
return suppressions.PresubmitCheck(input_api, output_api)
|
|
||||||
finally:
|
|
||||||
sys.path = old_path
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChangeOnUpload(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChangeOnCommit(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
|
|
||||||
def GetPreferredTrySlaves():
|
|
||||||
return ['win_drmemory']
|
|
|
@ -1,456 +0,0 @@
|
||||||
# This file contains suppressions for the Dr.Memory tool, see
|
|
||||||
# http://dev.chromium.org/developers/how-tos/using-drmemory
|
|
||||||
#
|
|
||||||
# This file contains suppressions for the DrMemory reports happening
|
|
||||||
# in the 'light' mode (a.k.a. drmemory_light) as well as in the 'full' mode.
|
|
||||||
# Please use suppressions_full.txt for all the reports that can happen only
|
|
||||||
# in the full mode (drmemory_full),
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Known reports on the third party we have no control over.
|
|
||||||
|
|
||||||
# Reports from Sophos antivirus
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=Sophos UNADDR
|
|
||||||
...
|
|
||||||
sophos*.dll!*
|
|
||||||
|
|
||||||
UNINITIALIZED READ
|
|
||||||
name=Sophos UNINIT
|
|
||||||
...
|
|
||||||
sophos*.dll!*
|
|
||||||
|
|
||||||
LEAK
|
|
||||||
name=Sophos LEAK
|
|
||||||
...
|
|
||||||
sophos*.dll!*
|
|
||||||
|
|
||||||
# Reports from Micorosft RDP ActiveX control (mstscax.dll)
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=crbug.com/177832: mstscax.dll causes "GDI USAGE ERROR" errors.
|
|
||||||
...
|
|
||||||
mstscax.dll!*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=crbug.com/177832: mstscax.dll causes "UNADDRESSABLE ACCESS" errors.
|
|
||||||
...
|
|
||||||
mstscax.dll!*
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Suppress some false reports due to bugs in Dr.Memory like wrong analysis
|
|
||||||
# assumptions or unhandled syscalls
|
|
||||||
|
|
||||||
# Please note: the following suppressions were written in the abscense of
|
|
||||||
# private symbols so may need to be updated when we switch to auto-loading PDBs
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=12 UNADDR
|
|
||||||
...
|
|
||||||
SHELL32.dll!SHFileOperation*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=40 UNADDR
|
|
||||||
...
|
|
||||||
WINSPOOL.DRV!*
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=40 INVALID HEAP
|
|
||||||
...
|
|
||||||
WINSPOOL.DRV!*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=59
|
|
||||||
...
|
|
||||||
*!SetEnvironmentVariable*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=68 (UNADDR 1)
|
|
||||||
...
|
|
||||||
MSWSOCK.dll!WSPStartup
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=68 (UNADDR 2)
|
|
||||||
...
|
|
||||||
ntdll.dll!RtlValidateUnicodeString
|
|
||||||
|
|
||||||
############################
|
|
||||||
# TODO(timurrrr): investigate these
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=TODO SHParseDisplayName
|
|
||||||
...
|
|
||||||
*!SHParseDisplayName
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=TODO GetCanonicalPathInfo
|
|
||||||
...
|
|
||||||
*!GetCanonicalPathInfo*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=TODO CreateDC
|
|
||||||
...
|
|
||||||
GDI32.dll!CreateDC*
|
|
||||||
|
|
||||||
# This one looks interesting
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=TODO ExitProcess
|
|
||||||
...
|
|
||||||
KERNEL32.dll!ExitProcess
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/103365 (a)
|
|
||||||
ppapi_tests.dll!*
|
|
||||||
...
|
|
||||||
ppapi_tests.dll!*
|
|
||||||
*!base::internal::RunnableAdapter<*>::Run
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/103365 (b)
|
|
||||||
ppapi_tests.dll!*
|
|
||||||
...
|
|
||||||
ppapi_tests.dll!*
|
|
||||||
*!PP_RunCompletionCallback
|
|
||||||
...
|
|
||||||
*!base::internal::RunnableAdapter<*>::Run
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/107567 intentional mismatch in _DebugHeapDelete, no frame
|
|
||||||
*!std::numpunct<*>::_Tidy
|
|
||||||
*!std::numpunct<*>::~numpunct<*>
|
|
||||||
|
|
||||||
# TODO(rbultje): Investigate if code fix is required instead.
|
|
||||||
WARNING
|
|
||||||
name=http://crbug.com/223255 - prefetches in vp8
|
|
||||||
instruction=prefetch*
|
|
||||||
ffmpegsumo.dll!ff_prefetch_mmxext
|
|
||||||
ffmpegsumo.dll!vp8_decode_mb_row_no_filter
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Intentional errors in Chromium tests (ToolsSanityTests)
|
|
||||||
LEAK
|
|
||||||
name=sanity test 01 (memory leak)
|
|
||||||
base_unittests.exe!operator new
|
|
||||||
base_unittests.exe!operator new[]
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_MemoryLeak_Test::TestBody
|
|
||||||
|
|
||||||
# "..." is needed due to http://code.google.com/p/drmemory/issues/detail?id=666
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 02 (malloc/read left)
|
|
||||||
base_unittests.exe!*ReadValueOutOfArrayBoundsLeft
|
|
||||||
...
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 03 (malloc/read right)
|
|
||||||
base_unittests.exe!*ReadValueOutOfArrayBoundsRight
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 04 (malloc/write left)
|
|
||||||
base_unittests.exe!*WriteValueOutOfArrayBoundsLeft
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 05 (malloc/write right)
|
|
||||||
base_unittests.exe!*WriteValueOutOfArrayBoundsRight
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
# "..." is needed due to http://code.google.com/p/drmemory/issues/detail?id=666
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 06 (new/read left)
|
|
||||||
base_unittests.exe!*ReadValueOutOfArrayBoundsLeft
|
|
||||||
...
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 07 (new/read right)
|
|
||||||
base_unittests.exe!*ReadValueOutOfArrayBoundsRight
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 08 (new/write left)
|
|
||||||
base_unittests.exe!*WriteValueOutOfArrayBoundsLeft
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 09 (new/write right)
|
|
||||||
base_unittests.exe!*WriteValueOutOfArrayBoundsRight
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 10 (write after free)
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=sanity test 11 (write after delete)
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=sanity test 12 (array deleted without [])
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_ArrayDeletedWithoutBraces_Test::TestBody
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=sanity test 13 (single element deleted with [])
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_SingleElementDeletedWithBraces_Test::TestBody
|
|
||||||
|
|
||||||
UNINITIALIZED READ
|
|
||||||
name=sanity test 14 (malloc/read uninit)
|
|
||||||
base_unittests.exe!*ReadUninitializedValue
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToMallocMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNINITIALIZED READ
|
|
||||||
name=sanity test 15 (new/read uninit)
|
|
||||||
base_unittests.exe!*ReadUninitializedValue
|
|
||||||
base_unittests.exe!*MakeSomeErrors
|
|
||||||
base_unittests.exe!base::ToolsSanityTest_AccessesToNewMemory_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=AboutHandler::AboutCrash deliberate crash
|
|
||||||
# TODO(bruening): switch to annotation once have support for that
|
|
||||||
chrome.dll!AboutHandler::AboutCrash
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=RendererCrashTest.Crash deliberate crash
|
|
||||||
# function is small, little risk for false negative in rest of it
|
|
||||||
# TODO(bruening): switch to annotation once have support for that
|
|
||||||
chrome.dll!HandleRendererErrorTestParameters
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=NPAPITesterBase.NoHangIfInitCrashes deliberate crash
|
|
||||||
# function is small, little risk for false negative in rest of it
|
|
||||||
# TODO(bruening): switch to annotation once have support for that
|
|
||||||
npapi_test_plugin.dll!NPAPIClient::PluginClient::Initialize
|
|
||||||
|
|
||||||
# Deliberate NULL deref to crash the child process
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=CrashingChildProcess deliberate crash
|
|
||||||
*!CrashingChildProcess
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=::Crasher::Run deliberate crash
|
|
||||||
*!base::`anonymous namespace'::Crasher::Run
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Benign issues in Chromium
|
|
||||||
|
|
||||||
WARNING
|
|
||||||
name=http://crbug.com/72463 - prefetches in generated MemCopy
|
|
||||||
instruction=prefetch*
|
|
||||||
<not in a module>
|
|
||||||
chrome.dll!v8::internal::CopyChars*
|
|
||||||
|
|
||||||
WARNING
|
|
||||||
name=prefetches in NVD3DUM.dll
|
|
||||||
instruction=prefetch*
|
|
||||||
NVD3DUM.dll!*
|
|
||||||
|
|
||||||
WARNING
|
|
||||||
name=prefetches in igdumd32.dll
|
|
||||||
instruction=prefetch*
|
|
||||||
igdumd32.dll!*
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=582 bizarre cl-generated read-beyond-TOS
|
|
||||||
instruction=mov 0xfffffffc(%esp) -> %eax
|
|
||||||
chrome.dll!WebCore::RenderStyle::resetBorder*
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/101537
|
|
||||||
*!scoped_ptr<_TOKEN_USER>*
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/101717 (1)
|
|
||||||
*!scoped_ptr<_TOKEN_DEFAULT_DACL>*
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/101717 (2)
|
|
||||||
*!sandbox::PolicyBase::~PolicyBase
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/101717 (3)
|
|
||||||
*!scoped_ptr<_UNICODE_STRING>::~scoped_ptr<_UNICODE_STRING>
|
|
||||||
*!sandbox::GetHandleName
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/101717 (4)
|
|
||||||
*!scoped_ptr<_OBJECT_NAME_INFORMATION>::~scoped_ptr<_OBJECT_NAME_INFORMATION>
|
|
||||||
*!sandbox::GetPathFromHandle
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://code.google.com/p/drmemory/issues/detail?id=899 deleting bitmap which is probably safe
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
*!skia::`anonymous namespace'::Bitmap::~Bitmap
|
|
||||||
*!skia::`anonymous namespace'::Bitmap::`scalar deleting destructor'
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Real issues in Chromium
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://crbug.com/88213
|
|
||||||
*!base::win::ObjectWatcher::StopWatching
|
|
||||||
*!base::win::ObjectWatcher::WillDestroyCurrentMessageLoop
|
|
||||||
*!MessageLoop::~MessageLoop
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://crbug.com/96010
|
|
||||||
*!TestingProfile::FinishInit
|
|
||||||
*!TestingProfile::TestingProfile
|
|
||||||
*!BrowserAboutHandlerTest_WillHandleBrowserAboutURL_Test::TestBody
|
|
||||||
|
|
||||||
UNADDRESSABLE ACCESS
|
|
||||||
name=http://crbug.com/106522
|
|
||||||
npapi_test_plugin.dll!NPAPIClient::PluginTest::id
|
|
||||||
npapi_test_plugin.dll!NPAPIClient::ExecuteGetJavascriptUrlTest::TimerProc
|
|
||||||
|
|
||||||
# Bad GDI teardown sequence.
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/109963 a
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
# usually one or two GDI32.dll frames here but sometimes in light mode
|
|
||||||
# there are zero. still pretty narrow b/c of frames on either side.
|
|
||||||
...
|
|
||||||
*!skia::BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/109963 b
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
# usually one or two GDI32.dll frames here but sometimes in light mode
|
|
||||||
# there are zero. still pretty narrow b/c of frames on either side.
|
|
||||||
...
|
|
||||||
*!skia::BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/109963 c
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
GDI32.dll!DeleteDC
|
|
||||||
content.dll!*
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/109963 d
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
GDI32.dll!DeleteDC
|
|
||||||
*!base::internal::RunnableAdapter*
|
|
||||||
|
|
||||||
# GDI usage errors in 3rd-party components
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/119552 a
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
...
|
|
||||||
*!OmniboxViewWin::*
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/119552 b
|
|
||||||
system call Nt*
|
|
||||||
...
|
|
||||||
*!ATL::*
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/119552 c
|
|
||||||
# optional gdi32.dll frame followed by user32.dll
|
|
||||||
# TODO(bruening): once have
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=846
|
|
||||||
# I would do "gdi32.dll!...\nuser32.dll!*"
|
|
||||||
*32.dll!*
|
|
||||||
...
|
|
||||||
shell32.dll!SHGetFileInfoW
|
|
||||||
*!IconLoader::ReadIcon
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/119552 d
|
|
||||||
system call NtGdiDeleteObjectApp
|
|
||||||
gdi32.dll!DeleteObject
|
|
||||||
riched20.dll!*
|
|
||||||
riched20.dll!*
|
|
||||||
riched20.dll!*
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/120157
|
|
||||||
# "ReleaseDC called from different thread than GetDC"
|
|
||||||
system call NtUserCallOneParam.RELEASEDC
|
|
||||||
*!*FontCache::CacheElement::~CacheElement
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/158090
|
|
||||||
# "DC created by one thread and used by another"
|
|
||||||
...
|
|
||||||
content.dll!content::*::FontCache::PreCacheFont
|
|
||||||
content.dll!content::FontCacheDispatcher::OnPreCacheFont
|
|
||||||
content.dll!DispatchToMethod<content::FontCacheDispatcher*
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/158090 c#4
|
|
||||||
# ReleaseDC for DC called from different thread than the thread that called GetDC
|
|
||||||
system call NtUserCallOneParam.RELEASEDC
|
|
||||||
ui.dll!gfx::ReadColorProfile
|
|
||||||
ui.dll!gfx::GetColorProfile
|
|
||||||
content.dll!content::RenderMessageFilter::OnGetMonitorColorProfile
|
|
||||||
content.dll!DispatchToMethod*
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/158350
|
|
||||||
# allocated with operator new[], freed with operator delete
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
content.dll!*
|
|
||||||
content.dll!*
|
|
||||||
content.dll!*
|
|
||||||
content.dll!*
|
|
||||||
content.dll!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
*!*
|
|
||||||
KERNEL32.dll!*
|
|
||||||
ntdll.dll!*
|
|
||||||
ntdll.dll!*
|
|
||||||
|
|
||||||
WARNING
|
|
||||||
name=Security test (new oveflow)
|
|
||||||
MSVCR100D.dll!operator new
|
|
||||||
*!operator new
|
|
||||||
*!operator new[]
|
|
||||||
*!`anonymous namespace'::SecurityTest_NewOverflow_Test::TestBody
|
|
||||||
*!testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,void>
|
|
||||||
|
|
||||||
WARNING
|
|
||||||
name=Security test (calloc overflow)
|
|
||||||
*!`anonymous namespace'::CallocReturnsNull
|
|
||||||
*!`anonymous namespace'::SecurityTest_CallocOverflow_Test::TestBody
|
|
||||||
*!testing::internal::HandleExceptionsInMethodIfSupported<testing::Test,void>
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/234484
|
|
||||||
# "DC created by one thread and used by another"
|
|
||||||
...
|
|
||||||
*!chrome::`anonymous namespace'::SetOverlayIcon
|
|
||||||
|
|
||||||
INVALID HEAP ARGUMENT
|
|
||||||
name=http://crbug.com/262088
|
|
||||||
drmemorylib.dll!av_dup_packet
|
|
||||||
msvcrt.dll!wcsrchr
|
|
||||||
ntdll.dll!RtlIsCurrentThreadAttachExempt
|
|
||||||
ntdll.dll!LdrShutdownThread
|
|
||||||
ntdll.dll!RtlExitUserThread
|
|
||||||
|
|
||||||
GDI USAGE ERROR
|
|
||||||
name=http://crbug.com/266484
|
|
||||||
skia.dll!HDCOffscreen::draw
|
|
||||||
skia.dll!SkScalerContext_GDI::generateImage
|
|
||||||
skia.dll!SkScalerContext::getImage
|
|
||||||
skia.dll!SkGlyphCache::findImage
|
|
||||||
skia.dll!D1G_NoBounder_RectClip
|
|
||||||
skia.dll!SkDraw::drawText
|
|
||||||
skia.dll!SkDevice::drawText
|
|
||||||
skia.dll!SkCanvas::drawText
|
|
||||||
media.dll!media::FakeVideoCaptureDevice::OnCaptureTask
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,196 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# drmemory_analyze.py
|
|
||||||
|
|
||||||
''' Given a Dr. Memory output file, parses errors and uniques them.'''
|
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
import common
|
|
||||||
import hashlib
|
|
||||||
import logging
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
class DrMemoryError:
|
|
||||||
def __init__(self, report, suppression, testcase):
|
|
||||||
self._report = report
|
|
||||||
self._testcase = testcase
|
|
||||||
|
|
||||||
# Chromium-specific transformations of the suppressions:
|
|
||||||
# Replace 'any_test.exe' and 'chrome.dll' with '*', then remove the
|
|
||||||
# Dr.Memory-generated error ids from the name= lines as they don't
|
|
||||||
# make sense in a multiprocess report.
|
|
||||||
supp_lines = suppression.split("\n")
|
|
||||||
for l in xrange(len(supp_lines)):
|
|
||||||
if supp_lines[l].startswith("name="):
|
|
||||||
supp_lines[l] = "name=<insert_a_suppression_name_here>"
|
|
||||||
if supp_lines[l].startswith("chrome.dll!"):
|
|
||||||
supp_lines[l] = supp_lines[l].replace("chrome.dll!", "*!")
|
|
||||||
bang_index = supp_lines[l].find("!")
|
|
||||||
d_exe_index = supp_lines[l].find(".exe!")
|
|
||||||
if bang_index >= 4 and d_exe_index + 4 == bang_index:
|
|
||||||
supp_lines[l] = "*" + supp_lines[l][bang_index:]
|
|
||||||
self._suppression = "\n".join(supp_lines)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
output = self._report + "\n"
|
|
||||||
if self._testcase:
|
|
||||||
output += "The report came from the `%s` test.\n" % self._testcase
|
|
||||||
output += "Suppression (error hash=#%016X#):\n" % self.ErrorHash()
|
|
||||||
output += (" For more info on using suppressions see "
|
|
||||||
"http://dev.chromium.org/developers/how-tos/using-drmemory#TOC-Suppressing-error-reports-from-the-\n")
|
|
||||||
output += "{\n%s\n}\n" % self._suppression
|
|
||||||
return output
|
|
||||||
|
|
||||||
# This is a device-independent hash identifying the suppression.
|
|
||||||
# By printing out this hash we can find duplicate reports between tests and
|
|
||||||
# different shards running on multiple buildbots
|
|
||||||
def ErrorHash(self):
|
|
||||||
return int(hashlib.md5(self._suppression).hexdigest()[:16], 16)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self._suppression)
|
|
||||||
|
|
||||||
def __eq__(self, rhs):
|
|
||||||
return self._suppression == rhs
|
|
||||||
|
|
||||||
|
|
||||||
class DrMemoryAnalyzer:
|
|
||||||
''' Given a set of Dr.Memory output files, parse all the errors out of
|
|
||||||
them, unique them and output the results.'''
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.known_errors = set()
|
|
||||||
self.error_count = 0;
|
|
||||||
|
|
||||||
def ReadLine(self):
|
|
||||||
self.line_ = self.cur_fd_.readline()
|
|
||||||
|
|
||||||
def ReadSection(self):
|
|
||||||
result = [self.line_]
|
|
||||||
self.ReadLine()
|
|
||||||
while len(self.line_.strip()) > 0:
|
|
||||||
result.append(self.line_)
|
|
||||||
self.ReadLine()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def ParseReportFile(self, filename, testcase):
|
|
||||||
ret = []
|
|
||||||
|
|
||||||
# First, read the generated suppressions file so we can easily lookup a
|
|
||||||
# suppression for a given error.
|
|
||||||
supp_fd = open(filename.replace("results", "suppress"), 'r')
|
|
||||||
generated_suppressions = {} # Key -> Error #, Value -> Suppression text.
|
|
||||||
for line in supp_fd:
|
|
||||||
# NOTE: this regexp looks fragile. Might break if the generated
|
|
||||||
# suppression format slightly changes.
|
|
||||||
m = re.search("# Suppression for Error #([0-9]+)", line.strip())
|
|
||||||
if not m:
|
|
||||||
continue
|
|
||||||
error_id = int(m.groups()[0])
|
|
||||||
assert error_id not in generated_suppressions
|
|
||||||
# OK, now read the next suppression:
|
|
||||||
cur_supp = ""
|
|
||||||
for supp_line in supp_fd:
|
|
||||||
if supp_line.startswith("#") or supp_line.strip() == "":
|
|
||||||
break
|
|
||||||
cur_supp += supp_line
|
|
||||||
generated_suppressions[error_id] = cur_supp.strip()
|
|
||||||
supp_fd.close()
|
|
||||||
|
|
||||||
self.cur_fd_ = open(filename, 'r')
|
|
||||||
while True:
|
|
||||||
self.ReadLine()
|
|
||||||
if (self.line_ == ''): break
|
|
||||||
|
|
||||||
match = re.search("^Error #([0-9]+): (.*)", self.line_)
|
|
||||||
if match:
|
|
||||||
error_id = int(match.groups()[0])
|
|
||||||
self.line_ = match.groups()[1].strip() + "\n"
|
|
||||||
report = "".join(self.ReadSection()).strip()
|
|
||||||
suppression = generated_suppressions[error_id]
|
|
||||||
ret.append(DrMemoryError(report, suppression, testcase))
|
|
||||||
|
|
||||||
if re.search("SUPPRESSIONS USED:", self.line_):
|
|
||||||
self.ReadLine()
|
|
||||||
while self.line_.strip() != "":
|
|
||||||
line = self.line_.strip()
|
|
||||||
(count, name) = re.match(" *([0-9]+)x(?: \(leaked .*\))?: (.*)",
|
|
||||||
line).groups()
|
|
||||||
count = int(count)
|
|
||||||
self.used_suppressions[name] += count
|
|
||||||
self.ReadLine()
|
|
||||||
|
|
||||||
if self.line_.startswith("ASSERT FAILURE"):
|
|
||||||
ret.append(self.line_.strip())
|
|
||||||
|
|
||||||
self.cur_fd_.close()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def Report(self, filenames, testcase, check_sanity):
|
|
||||||
sys.stdout.flush()
|
|
||||||
# TODO(timurrrr): support positive tests / check_sanity==True
|
|
||||||
self.used_suppressions = defaultdict(int)
|
|
||||||
|
|
||||||
to_report = []
|
|
||||||
reports_for_this_test = set()
|
|
||||||
for f in filenames:
|
|
||||||
cur_reports = self.ParseReportFile(f, testcase)
|
|
||||||
|
|
||||||
# Filter out the reports that were there in previous tests.
|
|
||||||
for r in cur_reports:
|
|
||||||
if r in reports_for_this_test:
|
|
||||||
# A similar report is about to be printed for this test.
|
|
||||||
pass
|
|
||||||
elif r in self.known_errors:
|
|
||||||
# A similar report has already been printed in one of the prev tests.
|
|
||||||
to_report.append("This error was already printed in some "
|
|
||||||
"other test, see 'hash=#%016X#'" % r.ErrorHash())
|
|
||||||
reports_for_this_test.add(r)
|
|
||||||
else:
|
|
||||||
self.known_errors.add(r)
|
|
||||||
reports_for_this_test.add(r)
|
|
||||||
to_report.append(r)
|
|
||||||
|
|
||||||
common.PrintUsedSuppressionsList(self.used_suppressions)
|
|
||||||
|
|
||||||
if not to_report:
|
|
||||||
logging.info("PASS: No error reports found")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.flush()
|
|
||||||
logging.info("Found %i error reports" % len(to_report))
|
|
||||||
for report in to_report:
|
|
||||||
self.error_count += 1
|
|
||||||
logging.info("Report #%d\n%s" % (self.error_count, report))
|
|
||||||
logging.info("Total: %i error reports" % len(to_report))
|
|
||||||
sys.stdout.flush()
|
|
||||||
return -1
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
'''For testing only. The DrMemoryAnalyze class should be imported instead.'''
|
|
||||||
parser = optparse.OptionParser("usage: %prog [options] <files to analyze>")
|
|
||||||
parser.add_option("", "--source_dir",
|
|
||||||
help="path to top of source tree for this build"
|
|
||||||
"(used to normalize source paths in baseline)")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
if len(args) == 0:
|
|
||||||
parser.error("no filename specified")
|
|
||||||
filenames = args
|
|
||||||
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
return DrMemoryAnalyzer().Report(filenames, None, False)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
|
@ -1,87 +0,0 @@
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
''' A bunch of helper functions for querying gdb.'''
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
GDB_LINE_RE = re.compile(r'Line ([0-9]*) of "([^"]*)".*')
|
|
||||||
|
|
||||||
def _GdbOutputToFileLine(output_line):
|
|
||||||
''' Parse the gdb output line, return a pair (file, line num) '''
|
|
||||||
match = GDB_LINE_RE.match(output_line)
|
|
||||||
if match:
|
|
||||||
return match.groups()[1], match.groups()[0]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def ResolveAddressesWithinABinary(binary_name, load_address, address_list):
|
|
||||||
''' For each address, return a pair (file, line num) '''
|
|
||||||
commands = tempfile.NamedTemporaryFile()
|
|
||||||
commands.write('add-symbol-file "%s" %s\n' % (binary_name, load_address))
|
|
||||||
for addr in address_list:
|
|
||||||
commands.write('info line *%s\n' % addr)
|
|
||||||
commands.write('quit\n')
|
|
||||||
commands.flush()
|
|
||||||
gdb_commandline = 'gdb -batch -x %s 2>/dev/null' % commands.name
|
|
||||||
gdb_pipe = os.popen(gdb_commandline)
|
|
||||||
result = gdb_pipe.readlines()
|
|
||||||
|
|
||||||
address_count = 0
|
|
||||||
ret = {}
|
|
||||||
for line in result:
|
|
||||||
if line.startswith('Line'):
|
|
||||||
ret[address_list[address_count]] = _GdbOutputToFileLine(line)
|
|
||||||
address_count += 1
|
|
||||||
if line.startswith('No line'):
|
|
||||||
ret[address_list[address_count]] = (None, None)
|
|
||||||
address_count += 1
|
|
||||||
gdb_pipe.close()
|
|
||||||
commands.close()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
class AddressTable(object):
|
|
||||||
''' Object to do batched line number lookup. '''
|
|
||||||
def __init__(self):
|
|
||||||
self._load_addresses = {}
|
|
||||||
self._binaries = {}
|
|
||||||
self._all_resolved = False
|
|
||||||
|
|
||||||
def AddBinaryAt(self, binary, load_address):
|
|
||||||
''' Register a new shared library or executable. '''
|
|
||||||
self._load_addresses[binary] = load_address
|
|
||||||
|
|
||||||
def Add(self, binary, address):
|
|
||||||
''' Register a lookup request. '''
|
|
||||||
if binary == '':
|
|
||||||
logging.warn('adding address %s in empty binary?' % address)
|
|
||||||
if binary in self._binaries:
|
|
||||||
self._binaries[binary].append(address)
|
|
||||||
else:
|
|
||||||
self._binaries[binary] = [address]
|
|
||||||
self._all_resolved = False
|
|
||||||
|
|
||||||
def ResolveAll(self):
|
|
||||||
''' Carry out all lookup requests. '''
|
|
||||||
self._translation = {}
|
|
||||||
for binary in self._binaries.keys():
|
|
||||||
if binary != '' and binary in self._load_addresses:
|
|
||||||
load_address = self._load_addresses[binary]
|
|
||||||
addr = ResolveAddressesWithinABinary(
|
|
||||||
binary, load_address, self._binaries[binary])
|
|
||||||
self._translation[binary] = addr
|
|
||||||
self._all_resolved = True
|
|
||||||
|
|
||||||
def GetFileLine(self, binary, addr):
|
|
||||||
''' Get the (filename, linenum) result of a previously-registered lookup
|
|
||||||
request.
|
|
||||||
'''
|
|
||||||
if self._all_resolved:
|
|
||||||
if binary in self._translation:
|
|
||||||
if addr in self._translation[binary]:
|
|
||||||
return self._translation[binary][addr]
|
|
||||||
return (None, None)
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -1,26 +0,0 @@
|
||||||
# TODO(timurrrr) investigate the failures and enable these tests one-by-one.
|
|
||||||
RSA*
|
|
||||||
GmockTest.*
|
|
||||||
EtwTrace*
|
|
||||||
StatsTableTest.*
|
|
||||||
ProcessUtilTest.EnableLFH
|
|
||||||
ScopedNativeLibrary.Basic
|
|
||||||
# TODO(zhaoqin) investigate the failures and enable it later, 106043
|
|
||||||
ConditionVariableTest.LargeFastTaskTest
|
|
||||||
# Next test creates a child that crashes, which naturally generates an
|
|
||||||
# unaddressable report as well as a handful of leak reports that we don't need
|
|
||||||
# to see.
|
|
||||||
ProcessUtilTest.GetTerminationStatusCrash
|
|
||||||
# See crbug.com/130668
|
|
||||||
ProcessUtilTest.GetTerminationStatusKill
|
|
||||||
ProcessUtilTest.KillSlowChild
|
|
||||||
ProcessUtilTest.SpawnChild
|
|
||||||
ScopedProcessInformationTest.Duplicate
|
|
||||||
ScopedProcessInformationTest.Swap
|
|
||||||
ScopedProcessInformationTest.TakeBoth
|
|
||||||
ScopedProcessInformationTest.TakeProcess
|
|
||||||
ScopedProcessInformationTest.TakeWholeStruct
|
|
||||||
SharedMemoryProcessTest.Tasks
|
|
||||||
|
|
||||||
# crbug/144018
|
|
||||||
ScopedStartupInfoExTest.InheritStdOut
|
|
|
@ -1,10 +0,0 @@
|
||||||
# Don't run this test under TSan, it takes ~1-2 minutes to pass.
|
|
||||||
ProcessUtilTest.GetAppOutputRestrictedNoZombies
|
|
||||||
|
|
||||||
# Don't run Memcheck sanity tests under ThreadSanitizer since they can
|
|
||||||
# corrupt memory.
|
|
||||||
ToolsSanityTest.*Memory*
|
|
||||||
ToolsSanityTest.*Delete*
|
|
||||||
|
|
||||||
# TSan doesn't understand SharedMemory locks, see http://crbug.com/45083
|
|
||||||
StatsTableTest.*MultipleThreads
|
|
|
@ -1,2 +0,0 @@
|
||||||
# http://crbug.com/29855.
|
|
||||||
StackTrace.OutputToStream
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Occasionally fails under TSan, see http://crbug.com/54229
|
|
||||||
ProcessUtilTest.CalcFreeMemory
|
|
||||||
|
|
||||||
# This file is copied from Valgrind-on-Wine filter
|
|
||||||
# TODO(timurrrr): include/investigate the listed tests one-by-one
|
|
||||||
EtwTraceControllerTest.EnableDisable
|
|
||||||
EtwTraceConsumer*Test.*
|
|
||||||
EtwTraceProvider*Test.*
|
|
||||||
JSONReaderTest.Reading
|
|
||||||
TimeTicks.*
|
|
||||||
WMIUtilTest.*
|
|
||||||
|
|
||||||
# Too slow under TSan
|
|
||||||
ConditionVariableTest.LargeFastTaskTest
|
|
||||||
|
|
||||||
# Fails under TSan: http://crbug.com/93843
|
|
||||||
MessageLoopTest.RecursiveDenial3
|
|
||||||
|
|
||||||
# Crashes under TSan: http://crbug.com/115107
|
|
||||||
WorkerPoolTest.PostTask
|
|
||||||
|
|
||||||
# Times out on Win7, slow on Vista: http://crbug.com/106531
|
|
||||||
TraceEventTestFixture.DataCapturedManyThreads
|
|
|
@ -1,26 +0,0 @@
|
||||||
# This test currently times out in valgrind, see http://crbug.com/9194
|
|
||||||
WatchdogTest.AlarmTest
|
|
||||||
|
|
||||||
# These tests occassionally hangs under Valgrind on Mac. valgrind-darwin r9573
|
|
||||||
# Revisit with better valgrind.
|
|
||||||
# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=189661
|
|
||||||
TimerTest.RepeatingTimer
|
|
||||||
TimerTest.RepeatingTimer_Cancel
|
|
||||||
|
|
||||||
# Crashes occasionally, see http://crbug.com/7477
|
|
||||||
ObserverListThreadSafeTest.CrossThreadObserver
|
|
||||||
ObserverListThreadSafeTest.CrossThreadNotifications
|
|
||||||
|
|
||||||
# Hangs sometimes on linux, see http://crbug.com/22138
|
|
||||||
ClipboardTest.*
|
|
||||||
|
|
||||||
# These tests trigger a CHECK so they will leak memory. They don't test
|
|
||||||
# anything else, so just disable them on valgrind. Bug 28179.
|
|
||||||
OutOfMemoryDeathTest.*
|
|
||||||
|
|
||||||
# Flaky under slow tools or just when the VM is under load.
|
|
||||||
# See http://crbug.com/43972
|
|
||||||
ConditionVariableTest.LargeFastTaskTest
|
|
||||||
|
|
||||||
# Flaky under Valgrind, see http://crbug.com/55517
|
|
||||||
PlatformFile.TouchGetInfoPlatformFile
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Fails on Valgrind/Mac, see http://crbug.com/43972
|
|
||||||
ConditionVariableTest.LargeFastTaskTest
|
|
||||||
|
|
||||||
# Fails on Valgrind/Mac due to missing syscall wrapper
|
|
||||||
# for the symlink() syscall. See http://crbug.com/44001
|
|
||||||
FileUtilTest.NormalizeFilePathSymlinks
|
|
||||||
|
|
||||||
# Fails on Valgrind/Mac, see http://crbug.com/53196
|
|
||||||
CancellationFlagTest.SetOnDifferentThreadDeathTest
|
|
||||||
|
|
||||||
# Fails on Valgrind/Mac, see http://crbug.com/93722
|
|
||||||
ProcessMemoryTest.MacTerminateOnHeapCorruption
|
|
||||||
|
|
||||||
# Fails on Valgrind/Mac, see http://crbug.com/122080
|
|
||||||
ProcessMemoryTest.MacMallocFailureDoesNotTerminate
|
|
||||||
|
|
||||||
# Times out on Valgrind/Mac, see http://crbug.com/172044
|
|
||||||
MessageLoopTest.RecursivePosts
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Fails natively as well: http://crbug.com/251517
|
|
||||||
PEImageTest.EnumeratesPE
|
|
|
@ -1,12 +0,0 @@
|
||||||
# Too slow under Valgrind/Wine and TSan/Windows
|
|
||||||
TimeTicks.WinRollover
|
|
||||||
|
|
||||||
# Very sensitive to slowdown
|
|
||||||
TimeTicks.Deltas
|
|
||||||
TimeTicks.HighResNow
|
|
||||||
TimerTest.RepeatingTimer*
|
|
||||||
|
|
||||||
# This Windows-native sampling profiler test does not work under our tools
|
|
||||||
# because it assumes the original code runs, not the modified version
|
|
||||||
# with instrumentation. See http://crbug.com/106829
|
|
||||||
SamplingProfilerTest.Sample
|
|
|
@ -1,50 +0,0 @@
|
||||||
# TODO(zhaoqin): File bugs for those failing browser tests.
|
|
||||||
|
|
||||||
# Dr.Memory i#1052: http://code.google.com/p/drmemory/issues/detail?id=1052
|
|
||||||
#
|
|
||||||
# The list is too long for gtest_filter, so we exclude the whole
|
|
||||||
# test case if any of its tests failed.
|
|
||||||
*FLAKY*
|
|
||||||
|
|
||||||
# it takes too long to run all browser_tests with Dr.Memory,
|
|
||||||
# and we only select subset to run
|
|
||||||
# A*: ~70 tests
|
|
||||||
A*
|
|
||||||
# DrM-i#1052-c#52
|
|
||||||
# AutofillTest.*
|
|
||||||
# AcceleratedCompositingBlockedTest.*
|
|
||||||
# AppApiTest.*
|
|
||||||
# BrowserAccessibilityStateImplTest.*
|
|
||||||
B*
|
|
||||||
C*
|
|
||||||
D*
|
|
||||||
E*
|
|
||||||
F*
|
|
||||||
G*
|
|
||||||
# H*: ~35 tests
|
|
||||||
H*
|
|
||||||
# DrM-i#1052-c#53
|
|
||||||
# HistoryWebUITest.*
|
|
||||||
# I*: ~10 tests
|
|
||||||
# DrM-i#1052-c#53
|
|
||||||
InfoBarsTest.*
|
|
||||||
# J*: 0 tests
|
|
||||||
# K*: 1 tests
|
|
||||||
L*
|
|
||||||
M*
|
|
||||||
N*
|
|
||||||
O*
|
|
||||||
P*
|
|
||||||
Q*
|
|
||||||
R*
|
|
||||||
S*
|
|
||||||
T*
|
|
||||||
# U*: ~20 tests
|
|
||||||
# DrM-i#1052-c#53
|
|
||||||
UnloadTest.*
|
|
||||||
# V*: 5 tests
|
|
||||||
# W*: ~150 tests
|
|
||||||
W*
|
|
||||||
# X*: 0 tests
|
|
||||||
# Y*: 0 tests
|
|
||||||
# Z*: 0 tests
|
|
|
@ -1,60 +0,0 @@
|
||||||
# Don't run FLAKY or FAILS ui tests under Valgrind.
|
|
||||||
# They tend to generate way too many flaky Valgrind reports.
|
|
||||||
*FLAKY_*
|
|
||||||
*FAILS_*
|
|
||||||
|
|
||||||
# NaCl tests fail with Data Execution Prevention error http://crbug.com/104517
|
|
||||||
NaClGdbTest.Empty
|
|
||||||
PPAPINaClGLibcTest.*
|
|
||||||
PPAPINaClNewlibTest.*
|
|
||||||
PPAPINaClTest*
|
|
||||||
|
|
||||||
# http://crbug.com/109336
|
|
||||||
OutOfProcessPPAPITest.View_PageHideShow
|
|
||||||
|
|
||||||
# TODO(thestig) File bugs for these failing browser tests.
|
|
||||||
AllUrlsApiTest.WhitelistedExtension
|
|
||||||
AppBackgroundPageApiTest.NoJsManifestBackgroundPage
|
|
||||||
BrowserCloseTest.DownloadsCloseCheck_2
|
|
||||||
BrowserCloseTest.DownloadsCloseCheck_5
|
|
||||||
BrowserEncodingTest.SLOW_TestEncodingAliasMapping
|
|
||||||
BrowserNavigatorTest.Disposition_Bookmarks_DoNothingIfIncognitoIsForced
|
|
||||||
BrowserNavigatorTest.Disposition_Incognito
|
|
||||||
BrowserNavigatorTest.Disposition_SyncPromo_DoNothingIfIncognitoIsForced
|
|
||||||
BrowserTest.ForwardDisabledOnForward
|
|
||||||
ClickToPlayPluginTest.Basic
|
|
||||||
ClickToPlayPluginTest.LoadAllBlockedPlugins
|
|
||||||
ClickToPlayPluginTest.NoCallbackAtLoad
|
|
||||||
DevToolsExperimentalExtensionTest.TestDevToolsExperimentalExtensionAPI
|
|
||||||
DevToolsExtensionTest.TestDevToolsExtensionMessaging
|
|
||||||
DownloadExtensionTest.DownloadExtensionTest_FileIcon_Active
|
|
||||||
DownloadExtensionTest.DownloadExtensionTest_FileIcon_History
|
|
||||||
DownloadExtensionTest.DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito
|
|
||||||
DownloadExtensionTestIncognito.DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito
|
|
||||||
ErrorPageTest.DNSError_Basic
|
|
||||||
ErrorPageTest.DNSError_GoBack1
|
|
||||||
ExecuteScriptApiTest.ExecuteScriptPermissions
|
|
||||||
ExtensionApiTest.FontSettingsIncognito
|
|
||||||
ExtensionApiTest.PopupBlockingExtension
|
|
||||||
ExtensionApiTest.PopupBlockingHostedApp
|
|
||||||
FastShutdown.SlowTermination
|
|
||||||
IndexedDBLayoutTest.IndexTests
|
|
||||||
NetInternalsTest.netInternalsPrerenderViewFail
|
|
||||||
NewTabUIBrowserTest.LoadNTPInExistingProcess
|
|
||||||
OutOfProcessPPAPITest.NetAddressPrivate_GetAnyAddress
|
|
||||||
OutOfProcessPPAPITest.NetAddressPrivate_ReplacePort
|
|
||||||
PageCyclerCachedBrowserTest.PlaybackMode
|
|
||||||
PageCyclerCachedBrowserTest.URLNotInCache
|
|
||||||
PPAPITest.ImeInputEvent
|
|
||||||
PrerenderBrowserTest.*
|
|
||||||
PrerenderBrowserTestWithNaCl.PrerenderNaClPluginEnabled
|
|
||||||
PrintPreviewWebUITest.TestPrinterList
|
|
||||||
PrintPreviewWebUITest.TestPrinterListCloudEmpty
|
|
||||||
PrintPreviewWebUITest.TestSectionsDisabled
|
|
||||||
PrintWebViewHelperTest.BlockScriptInitiatedPrinting
|
|
||||||
SafeBrowsingBlockingPageTest.MalwareDontProceed
|
|
||||||
SafeBrowsingBlockingPageTest.ProceedDisabled
|
|
||||||
SocketApiTest.SocketTCPExtension
|
|
||||||
SocketApiTest.SocketUDPExtension
|
|
||||||
SSLUITest.TestWSSInvalidCertAndGoForward
|
|
||||||
WebViewTest.Shim
|
|
|
@ -1,2 +0,0 @@
|
||||||
# http://crbug.com/159234.
|
|
||||||
WebContentsVideoCaptureDeviceTest.*
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Flaky, see http://crbug.com/227278
|
|
||||||
WebContentsVideoCaptureDeviceTest.WebContentsDestroyed
|
|
||||||
CompositingIOSurfaceTransformerTest.*
|
|
|
@ -1,52 +0,0 @@
|
||||||
# http://crbug.com/93245
|
|
||||||
GeolocationGatewayDataProviderCommonTest.*
|
|
||||||
GeolocationWifiDataProviderCommonTest.*
|
|
||||||
|
|
||||||
# Flaky, see http://crbug.com/131154
|
|
||||||
WebRTCAudioDeviceTest.FullDuplexAudioWithAGC
|
|
||||||
|
|
||||||
# Flaky, see http://crbug.com/155284
|
|
||||||
WebRTCAudioDeviceTest.StartRecording
|
|
||||||
WebRTCAudioDeviceTest.PlayLocalFile
|
|
||||||
|
|
||||||
# Fail/crash, see http://crbug.com/151939
|
|
||||||
WebDragDestTest.URL
|
|
||||||
WebDragDestTest.Data
|
|
||||||
WebDragSourceMacTest.DragInvalidlyEscapedBookmarklet
|
|
||||||
|
|
||||||
# Fail, see http://crbug.com/153007
|
|
||||||
MacSandboxTest.ClipboardAccess
|
|
||||||
|
|
||||||
# mach_override assertion, see http://crbug.com/162728
|
|
||||||
BlobURLRequestJobTest.*
|
|
||||||
|
|
||||||
# Fail, see http://crbug.com/159234
|
|
||||||
WebContentsVideoCaptureDeviceTest.GoesThroughAllTheMotions
|
|
||||||
WebContentsVideoCaptureDeviceTest.BadFramesGoodFrames
|
|
||||||
|
|
||||||
# Hang at arbitrary point, can't tell where exactly, see http://crbug.com/163314
|
|
||||||
RenderWidgetHostViewMacTest.*
|
|
||||||
WebContentsVideoCaptureDeviceTest.*
|
|
||||||
RenderViewHostTest.*
|
|
||||||
DeviceMotionEventPumpTest.*
|
|
||||||
|
|
||||||
# Speculative disable of hanging tests. http://crbug.com/241919
|
|
||||||
VideoCaptureControllerTest.*
|
|
||||||
VideoCaptureHostTest.*
|
|
||||||
|
|
||||||
# Hangs under Valgrind, see http://crbug.com/244257
|
|
||||||
SmoothScrollGestureControllerTest.Tick
|
|
||||||
|
|
||||||
# http://crbug.com/247163
|
|
||||||
VideoCaptureManagerTest.CloseWithoutStop
|
|
||||||
VideoCaptureManagerTest.CreateAndClose
|
|
||||||
VideoCaptureManagerTest.StartUsingId
|
|
||||||
WebRTCAudioDeviceTest.WebRtcPlayoutSetupTime
|
|
||||||
WebRTCAudioDeviceTest.WebRtcRecordingSetupTime
|
|
||||||
|
|
||||||
# http://crbug.com/247601
|
|
||||||
FontSerializationTest.StyledFonts
|
|
||||||
MacSandboxTest.FontLoadingTest
|
|
||||||
|
|
||||||
# http://crbug.com/270254
|
|
||||||
DeviceOrientationEventPumpTest.*
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Too slow under TSan
|
|
||||||
RSAPrivateKeyUnitTest.*
|
|
|
@ -1,34 +0,0 @@
|
||||||
# These test fail due to mmap Valgrind failures, see http://crbug.com/66677
|
|
||||||
CollectedCookiesTest.DoubleDisplay
|
|
||||||
CollectedCookiesTest.NavigateAway
|
|
||||||
InfoBarsUITest.TestInfoBarsCloseOnNewTheme
|
|
||||||
FastShutdown.SlowTermination
|
|
||||||
MouseLeaveTest.TestOnMouseOut
|
|
||||||
NotificationsPermissionTest.TestNoUserGestureInfobar
|
|
||||||
NotificationsPermissionTest.TestUserGestureInfobar
|
|
||||||
|
|
||||||
# These test fail due to timeout or limited buildslave support;
|
|
||||||
# http://crbug.com/67301
|
|
||||||
BrowserFocusTest.InterstitialFocus
|
|
||||||
BrowserFocusTest.FindFocusTest
|
|
||||||
BrowserFocusTest.FocusTraversalOnInterstitial
|
|
||||||
|
|
||||||
# Don't run FLAKY or FAILS tests under Valgrind and TSan
|
|
||||||
# as they tend to generate too many reports, see http://crbug.com/67959
|
|
||||||
# NB: Can't use FAILS_/FLAKY_ as it will be turned into *.* by chrome_tests.py!
|
|
||||||
*.FLAKY*
|
|
||||||
*.FAILS*
|
|
||||||
|
|
||||||
# Fails under Valgrind, see http://crbug.com/68068
|
|
||||||
DevToolsSanityTest.TestPauseWhenScriptIsRunning
|
|
||||||
|
|
||||||
# These tests time out under Valgrind, see http://crbug.com/163880
|
|
||||||
BrowserFocusTest.FocusOnReload
|
|
||||||
CommandsApiTest.Basic
|
|
||||||
ExtensionApiTest.NotificationsHasPermissionManifest
|
|
||||||
ExtensionCrashRecoveryTest.ReloadTabsWithBackgroundPage
|
|
||||||
ExtensionCrashRecoveryTest.TwoExtensionsCrashBothAtOnce
|
|
||||||
ExtensionCrashRecoveryTest.TwoExtensionsCrashFirst
|
|
||||||
ExtensionCrashRecoveryTest.TwoExtensionsOneByOne
|
|
||||||
FullscreenControllerInteractiveTest.TestTabExitsMouseLockOnNavigation
|
|
||||||
OmniboxViewTest.Escape
|
|
|
@ -1,2 +0,0 @@
|
||||||
# TODO(timurrrr): investigate
|
|
||||||
IPCSyncChannelTest.*
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Fails under TSan, see http://crbug.com/62511
|
|
||||||
IPCSyncChannelTest.BadMessage
|
|
|
@ -1,6 +0,0 @@
|
||||||
# Takes 27-40 seconds to run.
|
|
||||||
IPCSyncChannelTest.ChattyServer
|
|
||||||
# Hangs on Linux sometimes. See http://crbug.com/22141
|
|
||||||
IPCChannelTest.ChannelTest
|
|
||||||
# Crashes under Valgrind. See http://crbug.com/46782
|
|
||||||
IPCSyncChannelTest.Multiple
|
|
|
@ -1,4 +0,0 @@
|
||||||
# Hangs under Dr. Memory
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=978
|
|
||||||
WinAudioTest.SyncSocketBasic
|
|
||||||
AudioBusTest.CopyTo
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Win TSan disturbs ffmpeg's output, causing hash comparison assertion to fail.
|
|
||||||
# http://crbug.com/120396
|
|
||||||
PipelineIntegrationTest.BasicPlaybackHashed
|
|
|
@ -1,3 +0,0 @@
|
||||||
# This test tries to record fake audio in real-time.
|
|
||||||
# This appears to be too sensitive to slowdown, see http://crbug.com/49497
|
|
||||||
FakeAudioInputTest.BasicCallbacks
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Fails http://crbug.com/256911
|
|
||||||
MessageCenterImplTest.PopupTimersControllerResetTimer
|
|
||||||
MessageCenterImplTest.PopupTimersControllerStartMultipleTimersPause
|
|
|
@ -1,3 +0,0 @@
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=842
|
|
||||||
# Failing and then crashing.
|
|
||||||
HttpNetworkTransationSpdy21Test.HttpsProxySpdy*
|
|
|
@ -1,24 +0,0 @@
|
||||||
# See http://crbug.com/82391
|
|
||||||
URLRequestTestHTTP.HTTPSToHTTPRedirectNoRefererTest
|
|
||||||
|
|
||||||
# Times out. See http://crbug.com/134313
|
|
||||||
URLRequestTestHTTP.GetTest_ManyCookies
|
|
||||||
|
|
||||||
# Dr. Memory hits an assertion:
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=422
|
|
||||||
HttpAuthTest.*
|
|
||||||
HttpAuthHandlerFactoryTest.*
|
|
||||||
X509CertificateTest.*
|
|
||||||
|
|
||||||
# Too many uninits and too slow. TODO(timurrrr): investigate uninits
|
|
||||||
ProxyResolverV8Test.*
|
|
||||||
|
|
||||||
# Slow
|
|
||||||
CookieMonsterTest.GarbageCollectionTriggers
|
|
||||||
|
|
||||||
# Hangs only when built in release mode.
|
|
||||||
# http://crbug.com/105762
|
|
||||||
ClientSocketPoolBaseTest.DisableCleanupTimer
|
|
||||||
|
|
||||||
# Flaky, see http://crbug.com/108422
|
|
||||||
SSLClientSocketTest.ConnectMismatched
|
|
|
@ -1,26 +0,0 @@
|
||||||
# These tests leak data intentionally, so are inappropriate for Valgrind tests.
|
|
||||||
# Similar list in ../purify/net_unittests.exe.gtest.txt
|
|
||||||
# TODO(dkegel): either merge the two files or keep them in sync,
|
|
||||||
# see http://code.google.com/p/chromium/issues/detail?id=8951
|
|
||||||
DiskCacheBackendTest.AppCacheInvalidEntry
|
|
||||||
DiskCacheBackendTest.AppCacheInvalidEntryRead
|
|
||||||
DiskCacheBackendTest.AppCacheInvalidEntryWithLoad
|
|
||||||
DiskCacheBackendTest.InvalidEntry
|
|
||||||
DiskCacheBackendTest.InvalidEntryRead
|
|
||||||
DiskCacheBackendTest.InvalidEntryWithLoad
|
|
||||||
DiskCacheBackendTest.TrimInvalidEntry
|
|
||||||
DiskCacheBackendTest.TrimInvalidEntry2
|
|
||||||
DiskCacheBackendTest.InvalidEntryEnumeration
|
|
||||||
DiskCacheBackendTest.NewEvictionInvalidEntry
|
|
||||||
DiskCacheBackendTest.NewEvictionInvalidEntryRead
|
|
||||||
DiskCacheBackendTest.NewEvictionInvalidEntryWithLoad
|
|
||||||
DiskCacheBackendTest.NewEvictionTrimInvalidEntry
|
|
||||||
DiskCacheBackendTest.NewEvictionTrimInvalidEntry2
|
|
||||||
DiskCacheBackendTest.NewEvictionInvalidEntryEnumeration
|
|
||||||
DiskCacheBackendTest.ShutdownWithPendingCreate_Fast
|
|
||||||
DiskCacheBackendTest.ShutdownWithPendingFileIO_Fast
|
|
||||||
DiskCacheBackendTest.ShutdownWithPendingIO_Fast
|
|
||||||
|
|
||||||
# flaky failure on Linux Tests (valgrind)(2),
|
|
||||||
# see http://code.google.com/p/chromium/issues/detail?id=117196
|
|
||||||
SSLClientSocketTest.VerifyReturnChainProperlyOrdered
|
|
|
@ -1,27 +0,0 @@
|
||||||
# These huge tests are flaky and sometimes crash the following tests.
|
|
||||||
# See http://crbug.com/50346
|
|
||||||
DiskCacheEntryTest.*HugeSparse*
|
|
||||||
|
|
||||||
# SPDY tests tend to crash on both Mac and Windows.
|
|
||||||
# See http://crbug.com/51144
|
|
||||||
Spdy/SpdyNetworkTransactionTest.SocketWriteReturnsZero*
|
|
||||||
# See http://crbug.com/50918
|
|
||||||
Spdy/SpdyNetworkTransactionTest.CancelledTransactionSendRst*
|
|
||||||
# See http://crbug.com/51087
|
|
||||||
Spdy*
|
|
||||||
|
|
||||||
# See http://crbug.com/44570
|
|
||||||
HttpNetworkTransactionTest.StopsReading204
|
|
||||||
# See http://crbug.com/51145
|
|
||||||
HttpNetworkTransactionTest.Incomplete100ThenEOF
|
|
||||||
HttpNetworkTransactionTest.UseAlternateProtocolForNpnSpdyWithExistingSpdySession
|
|
||||||
HttpNetworkTransactionTest.KeepAliveConnectionEOF
|
|
||||||
|
|
||||||
# Crashes silently, see http://crbug.com/76911
|
|
||||||
URLRequestTest.FileTest
|
|
||||||
|
|
||||||
# http://crbug.com/92439
|
|
||||||
ServerBoundCertServiceTest.*
|
|
||||||
|
|
||||||
# Flaky, see http://crbug.com/259781
|
|
||||||
EmbeddedTestServerTest.*
|
|
|
@ -1,11 +0,0 @@
|
||||||
# WebSocketTest tests are extraordinary slow under ThreadSanitizer,
|
|
||||||
# (see http://crbug.com/25392)
|
|
||||||
# TODO(glider): investigate this.
|
|
||||||
WebSocketTest.*
|
|
||||||
|
|
||||||
# Strange reports from __NSThread__main__ appeared with the new TSan binaries
|
|
||||||
# See http://crbug.com/38926
|
|
||||||
DirectoryLister*
|
|
||||||
|
|
||||||
# Looks like http://crbug.com/78536 depends on this test.
|
|
||||||
CookieMonsterTest.GarbageCollectionTriggers
|
|
|
@ -1,40 +0,0 @@
|
||||||
# These tests fail due to unknown reasons
|
|
||||||
# TODO(timurrrr): investigate
|
|
||||||
CookieMonsterTest.TestLastAccess
|
|
||||||
SpdyNetwork*Error*
|
|
||||||
SpdyNetwork*Get*
|
|
||||||
SpdyNetworkTransactionTest.SynReplyHeadersVary
|
|
||||||
X509CertificateTest.UnoSoftCertParsing
|
|
||||||
URLRequestTest.DoNotSaveCookies
|
|
||||||
URLRequestTest.QuitTest
|
|
||||||
|
|
||||||
# See http://crbug.com/47836
|
|
||||||
ClientSocketPoolBaseTest.CancelPendingSocketAtSocketLimit
|
|
||||||
|
|
||||||
# Single-threaded and relatively slow - no reason to test
|
|
||||||
# See http://crbug.com/59642
|
|
||||||
CookieMonsterTest.GarbageCollectionTriggers
|
|
||||||
|
|
||||||
# Time out, see http://crbug.com/68482
|
|
||||||
SSLServerSocketTest.*
|
|
||||||
|
|
||||||
# See http://crbug.com/102330
|
|
||||||
SSLClientSocketTest.*
|
|
||||||
|
|
||||||
# See http://crbug.com/104805
|
|
||||||
HostResolverImplTest.AbortOnlyExistingRequestsOnIPAddressChange
|
|
||||||
|
|
||||||
# Times out occasionally, http://crbug.com/124452
|
|
||||||
HostResolverImplTest.StartWithinCallback
|
|
||||||
|
|
||||||
# Crash. See crbug.com/234776.
|
|
||||||
DiskCacheEntryTest.EvictOldEntries
|
|
||||||
DiskCacheEntryTest.NewEvictionEvictOldEntries
|
|
||||||
|
|
||||||
# Hang. crbug.com/265647.
|
|
||||||
NetworkChangeNotifierWinTest.NetChangeWinBasic
|
|
||||||
NetworkChangeNotifierWinTest.NetChangeWinSignal
|
|
||||||
NetworkChangeNotifierWinTest.NetChangeWinFailSignal*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
# Very slow under Valgrind.
|
|
||||||
KeygenHandlerTest.*SmokeTest
|
|
||||||
KeygenHandlerTest.*ConcurrencyTest
|
|
||||||
|
|
||||||
# Hangs, see http://crbug.com/61908
|
|
||||||
DirectoryListerTest.BigDirRecursiveTest
|
|
||||||
|
|
||||||
# http://crbug.com/88228
|
|
||||||
SSLClientSocketTest.Connect
|
|
||||||
SSLClientSocketTest.ConnectClientAuthSendNullCert
|
|
||||||
|
|
||||||
# These tests are broken http://crbug.com/118883
|
|
||||||
*SpdyNetworkTransactionSpdy*Test.*
|
|
||||||
*SpdyHttpStreamSpdy*Test.*
|
|
||||||
|
|
||||||
# Fails flakily. http://crbug.com/255775
|
|
||||||
SimpleIndexFileTest.WriteThenLoadIndex
|
|
|
@ -1,5 +0,0 @@
|
||||||
# Flaky. crbug.com/234776
|
|
||||||
DiskCacheEntryTest.SimpleCacheStreamAccess
|
|
||||||
DiskCacheEntryTest.SimpleCacheGrowData
|
|
||||||
DiskCacheEntryTest.SimpleCacheSizeChanges
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
# Very slow under Valgrind, (see <http://crbug.com/37289>).
|
|
||||||
KeygenHandlerTest.SmokeTest
|
|
||||||
|
|
||||||
# These tests fail under Valgrind on Mac, see http://crbug.com/62314
|
|
||||||
SSLClientSocketTest.*
|
|
||||||
HTTPSRequestTest.*
|
|
|
@ -1,3 +0,0 @@
|
||||||
# CreateDC returns NULL, see http://crbug.com/73652
|
|
||||||
PrintingContextTest.Base
|
|
||||||
PrintingContextTest.PrintAll
|
|
|
@ -1,9 +0,0 @@
|
||||||
# This test fails on an assertion, see http://crbug.com/57266
|
|
||||||
EncoderVp8Test.TestEncoder
|
|
||||||
DecoderVp8Test.EncodeAndDecode
|
|
||||||
|
|
||||||
# These test intentionally generate exceptions to verify if a dump is generated
|
|
||||||
# during the crash.
|
|
||||||
BreakpadWinDeathTest.TestAccessViolation
|
|
||||||
BreakpadWinDeathTest.TestInvalidParameter
|
|
||||||
BreakpadWinDeathTest.TestDebugbreak
|
|
|
@ -1,2 +0,0 @@
|
||||||
# These tests load mstscax.dll, which generates a bunch of race reports, see http://crbug.com/177832
|
|
||||||
RdpClientTest.*
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Fails natively as well: http://crbug.com/251517
|
|
||||||
RdpClientTest.Basic
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Fails on Valgrind/Mac, see http://crbug.com/69280
|
|
||||||
SafeBrowsingServiceTest.SafeBrowsingSystemTest
|
|
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
Test DiskCacheBackendTest.InvalidEntryEnumeration leaks.
|
|
||||||
Memcheck:Leak
|
|
||||||
fun:_Znwj
|
|
||||||
fun:_ZN10disk_cache12StorageBlockINS_12RankingsNodeEE12AllocateDataEv
|
|
||||||
fun:_ZN10disk_cache12StorageBlockINS_12RankingsNodeEE4LoadEv
|
|
||||||
fun:_ZN10disk_cache9EntryImpl15LoadNodeAddressEv
|
|
||||||
fun:_ZN10disk_cache11BackendImpl8NewEntryENS_4AddrEPPNS_9EntryImplEPb
|
|
||||||
fun:_ZN10disk_cache11BackendImpl10MatchEntryERKSsjb
|
|
||||||
fun:_ZN10disk_cache11BackendImpl9OpenEntryERKSsPPNS_5EntryE
|
|
||||||
fun:_ZN49DiskCacheBackendTest_InvalidEntryEnumeration_Test8TestBodyEv
|
|
||||||
fun:_ZN7testing4Test3RunEv
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Test DiskCacheBackendTest.InvalidEntryRead leaks.
|
|
||||||
Memcheck:Leak
|
|
||||||
fun:_Znwj
|
|
||||||
fun:_ZN10disk_cache11BackendImpl8NewEntryENS_4AddrEPPNS_9EntryImplEPb
|
|
||||||
fun:_ZN10disk_cache11BackendImpl10MatchEntryERKSsjb
|
|
||||||
fun:_ZN10disk_cache11BackendImpl9OpenEntryERKSsPPNS_5EntryE
|
|
||||||
fun:_ZN42DiskCacheBackendTest_InvalidEntryRead_Test8TestBodyEv
|
|
||||||
fun:_ZN7testing4Test3RunEv
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Test DiskCacheBackendTest.InvalidEntryWithLoad leaks.
|
|
||||||
Memcheck:Leak
|
|
||||||
fun:_Znwj
|
|
||||||
fun:_ZN10disk_cache11BackendImpl11CreateEntryERKSsPPNS_5EntryE
|
|
||||||
fun:_ZN46DiskCacheBackendTest_InvalidEntryWithLoad_Test8TestBodyEv
|
|
||||||
fun:_ZN7testing4Test3RunEv
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Test FlipNetworkTransactionTest.WriteError Bug 29004
|
|
||||||
Memcheck:Leak
|
|
||||||
fun:_Znw*
|
|
||||||
...
|
|
||||||
fun:_ZN3net26FlipNetworkTransactionTest17TransactionHelperERKNS_15HttpRequestInfoEPNS_17DelayedSocketDataE
|
|
||||||
fun:_ZN3net42FlipNetworkTransactionTest_WriteError_Test8TestBodyEv
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Flaky, see http://crbug.com/118370
|
|
||||||
SyncSchedulerTest.TransientPollFailure
|
|
|
@ -1,5 +0,0 @@
|
||||||
# Flaky, see http://crbug.com/118370
|
|
||||||
SyncSchedulerTest.TransientPollFailure
|
|
||||||
|
|
||||||
# Flaky, http://crbug.com/119467
|
|
||||||
InvalidationNotifierTest.Basic
|
|
|
@ -1,2 +0,0 @@
|
||||||
# http://crbug.com/222606
|
|
||||||
RenderTextTest.DisplayRectShowsCursorLTR
|
|
|
@ -1,2 +0,0 @@
|
||||||
# Hangs under TSAN, see http://crbug.com/28332
|
|
||||||
TextEliderTest.ElideTextLongStrings
|
|
|
@ -1,7 +0,0 @@
|
||||||
# Crashing (!) since forever, needs analysis.
|
|
||||||
BookmarkNodeDataTest.*
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=842
|
|
||||||
# Fails assertion. App data corrupted by DrMemory?
|
|
||||||
JsonSchemaTest.TestType
|
|
||||||
JsonSchemaTest.TestNumber
|
|
|
@ -1,69 +0,0 @@
|
||||||
##################################################
|
|
||||||
# known Dr. Memory bugs:
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=318
|
|
||||||
AudioRendererHostTest.*
|
|
||||||
|
|
||||||
##################################################
|
|
||||||
# un-analyzed Dr. Memory bugs:
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=548
|
|
||||||
DownloadManagerTest.StartDownload
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=979
|
|
||||||
FirefoxProfileImporterTest.Firefox35Importer
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=980
|
|
||||||
MetricsLogManagerTest.*
|
|
||||||
|
|
||||||
# http://code.google.com/p/drmemory/issues/detail?id=983
|
|
||||||
ProfileShortcutManagerTest.*
|
|
||||||
|
|
||||||
##################################################
|
|
||||||
# Chromium bugs:
|
|
||||||
|
|
||||||
# times out on the bot
|
|
||||||
# http://crbug.com/87887
|
|
||||||
VideoCaptureHostTest.*
|
|
||||||
|
|
||||||
# crashes due to use-after-free's, http://crbug.com/90980
|
|
||||||
FirefoxImporterTest.Firefox*NSS3Decryptor
|
|
||||||
|
|
||||||
# fails http://crbug.com/92144
|
|
||||||
ServiceProcessStateTest.ForceShutdown
|
|
||||||
|
|
||||||
# fails sporadically: http://crbug.com/108205
|
|
||||||
MultiProcessLockTest.RecursiveLock
|
|
||||||
|
|
||||||
# Poor isolation, DCHECKs when no MessageLoop exists. Breaks when sharded.
|
|
||||||
# http://crbug.com/117679
|
|
||||||
WebsiteSettingsModelTest.*
|
|
||||||
|
|
||||||
# fails to create thread
|
|
||||||
# http://crbug.com/144087
|
|
||||||
DesktopNotificationServiceTest.SettingsForSchemes
|
|
||||||
TemplateURLFetcherTest.*
|
|
||||||
|
|
||||||
# times out on the bot.
|
|
||||||
# http://crbug.com/148644
|
|
||||||
GAIAInfoUpdateServiceTest.*
|
|
||||||
ProfileManagerTest.*
|
|
||||||
ProfileInfoCacheTest.*
|
|
||||||
|
|
||||||
# Failing on the bot. http://crbug.com/167014
|
|
||||||
BrowserCommandControllerTest.AvatarMenuDisabledWhenOnlyOneProfile
|
|
||||||
|
|
||||||
# Failing on the bot. http://crbug.com/168882
|
|
||||||
UserCloudPolicyStoreTest.LoadWithInvalidFile
|
|
||||||
UserCloudPolicyStoreTest.LoadWithNoFile
|
|
||||||
UserCloudPolicyStoreTest.Store
|
|
||||||
UserCloudPolicyStoreTest.StoreThenClear
|
|
||||||
UserCloudPolicyStoreTest.StoreThenLoad
|
|
||||||
UserCloudPolicyStoreTest.StoreTwoTimes
|
|
||||||
UserCloudPolicyStoreTest.StoreValidationError
|
|
||||||
|
|
||||||
# Tests are timing out on the bot. crbug.com/248373.
|
|
||||||
PnaclTranslationCacheTest.*
|
|
||||||
|
|
||||||
# Failing on the bot. crbug.com/266972
|
|
||||||
OneClickSigninBubbleViewTest.ShowBubble
|
|
|
@ -1,12 +0,0 @@
|
||||||
# Takes too long and may cause bots to time out. http://crbug.com/134400
|
|
||||||
# This test alone takes 10-15 minutes.
|
|
||||||
Convolver.SIMDVerification
|
|
||||||
|
|
||||||
# Timing issues. http://crbug.com/241051
|
|
||||||
ExtensionAlarmsTest.*
|
|
||||||
|
|
||||||
# SEGV_MAPERR. http://crbug.com/245797
|
|
||||||
ClientSideDetectionHostTest.NavigationCancelsShouldClassifyUrl
|
|
||||||
|
|
||||||
# Test fails on CrOS memcheck only. http://crbug.com/247440
|
|
||||||
NotificationAudioControllerTest.MultiProfiles
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This test has a possible data race detected by the TSAN bot
|
|
||||||
# see http://crbug.com/46840
|
|
||||||
ProfileManagerTest.CreateAndUseTwoProfiles
|
|
||||||
|
|
||||||
# Crashing - http://crbug.com/84536
|
|
||||||
HttpBridgeTest.*
|
|
||||||
|
|
||||||
# Takes too long and may cause TSAN bots to time out. http://crbug.com/134400
|
|
||||||
Convolver.SIMDVerification
|
|
||||||
|
|
||||||
# SEGV_MAPERR. http://crbug.com/245797
|
|
||||||
ClientSideDetectionHostTest.NavigationCancelsShouldClassifyUrl
|
|
|
@ -1,5 +0,0 @@
|
||||||
# http://crbug.com/26214
|
|
||||||
ExtensionTest.InitFromValueInvalid
|
|
||||||
|
|
||||||
# http://crbug.com/38503
|
|
||||||
TabRestoreServiceTest.DontPersistPostData
|
|
|
@ -1,36 +0,0 @@
|
||||||
# Hangs sometimes; see http://crbug.com/22146
|
|
||||||
VisitedLinkEventsTest.Coalescense
|
|
||||||
# Hangs sometimes; see http://crbug.com/22160
|
|
||||||
VisitedLinkRelayTest.Basics
|
|
||||||
# Hangs (or takes forever?) reliably on bots; see http://crbug.com/23580
|
|
||||||
RenderViewTest.ImeComposition
|
|
||||||
# Hangs sometimes; see http://crbug.com/52844
|
|
||||||
PredictorTest.MassiveConcurrentLookupTest
|
|
||||||
# Pure virtual method called: see http://crbug.com/50950
|
|
||||||
ConnectionTesterTest.RunAllTests
|
|
||||||
|
|
||||||
# Following tests fail under valgrind because libjingle has hardcoded
|
|
||||||
# timeouts for P2P connections, and it makes these tests fail under valgrind.
|
|
||||||
# TODO(sergeyu): Remove hardcoded timeouts from libjingle.
|
|
||||||
P2PTransportImplTest.Create
|
|
||||||
P2PTransportImplTest.ConnectUdp
|
|
||||||
P2PTransportImplTest.ConnectTcp
|
|
||||||
P2PTransportImplTest.SendDataUdp
|
|
||||||
P2PTransportImplTest.SendDataTcp
|
|
||||||
|
|
||||||
# Failing on CrOS, see http://crbug.com/79657
|
|
||||||
SignedSettingsTest.StorePolicyNoPolicyData
|
|
||||||
|
|
||||||
# Flaky and not very interesting under Valgrind http://crbug.com/93027
|
|
||||||
ProcessWatcherTest.ImmediateTermination
|
|
||||||
|
|
||||||
# Timing out all over the place. Disabling for now. http://crbug.com/149715
|
|
||||||
ExtensionWebRequestTest.*
|
|
||||||
# Timing out all over the place. Disabling for now. http://crbug.com/149882
|
|
||||||
NativeMessagingTest.*
|
|
||||||
|
|
||||||
# Timing out all over the place. Disabling for now. http://crbug.com/164589
|
|
||||||
StorageInfoProviderTest.*
|
|
||||||
|
|
||||||
# Fails under Valgrind, probably timing-related. http://crbug.com/259679
|
|
||||||
WhitelistManagerTest.DownloadWhitelistRetry
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Fails under Valgrind; see http://crbug.com/36770
|
|
||||||
URLFetcherBadHTTPSTest.BadHTTPSTest
|
|
||||||
# Fails under Valgrind; see http://crbug.com/44552
|
|
||||||
RenderViewTest.OnHandleKeyboardEvent
|
|
||||||
# http://crbug.com/88221
|
|
||||||
ConnectionTesterTest.DeleteWhileInProgress
|
|
||||||
# Crash on CrOS, see http://crbug.com/115979
|
|
||||||
ClientSideDetectionHostTest.OnPhishingDetectionDoneNotPhishing
|
|
||||||
ClientSideDetectionHostTest.OnPhishingDetectionDoneVerdictNotPhishing
|
|
||||||
ClientSideDetectionHostTest.OnPhishingDetectionDoneInvalidVerdict
|
|
||||||
ClientSideDetectionHostTest.OnPhishingDetectionDoneDisabled
|
|
||||||
|
|
||||||
# http://crbug.com/119610
|
|
||||||
ProfileSyncServiceSessionTest.WriteFilledSessionToNode
|
|
||||||
ProfileSyncServiceSessionTest.ValidTabs
|
|
||||||
|
|
||||||
# http://crbug.com/139652
|
|
||||||
BackgroundApplicationListModelTest.RandomTest
|
|
||||||
|
|
||||||
# http://crbug.com/179427
|
|
||||||
ExtensionPrefsDelayedInstallInfo.DelayedInstallInfo
|
|
||||||
ExtensionServiceTest.*
|
|
||||||
|
|
||||||
# http://crbug.com/180335
|
|
||||||
AutocompleteActionPredictorTest.RecommendActionURL
|
|
||||||
|
|
||||||
# http://crbug.com/180467
|
|
||||||
HttpPipeliningCompatibilityClientTest.*
|
|
||||||
|
|
||||||
# http://crbug.com/238964
|
|
||||||
CpuInfoProviderTest.*
|
|
||||||
|
|
||||||
# Fails flakily. http://crbug.com/255771
|
|
||||||
NetworkStatsTestUDP.UDPEcho*
|
|
|
@ -1,39 +0,0 @@
|
||||||
# Times out too often
|
|
||||||
# crbug.com/15817
|
|
||||||
IPCSyncChannelTest.*
|
|
||||||
# Hangs
|
|
||||||
# http://crbug.com/21890
|
|
||||||
WebDropTargetTest.URL
|
|
||||||
WebDropTargetTest.Data
|
|
||||||
# http://crbug.com/69037
|
|
||||||
FirefoxImporterTest.Firefox3NSS3Decryptor
|
|
||||||
# http://crbug.com/69039
|
|
||||||
ProcessInfoSnapshotMacTest.EffectiveVsRealUserIDTest
|
|
||||||
|
|
||||||
# Following tests do not pass memcheck test.
|
|
||||||
# See http://crbug.com/30393.
|
|
||||||
NSMenuItemAdditionsTest.TestMOnDifferentLayouts
|
|
||||||
|
|
||||||
# Hangs
|
|
||||||
# See http://crbug.com/75733
|
|
||||||
BookmarkBarControllerTest.DeleteFromOffTheSideWhileItIsOpen
|
|
||||||
BookmarkBarControllerTest.HideWhenShowBookmarkBarTrueButDisabled
|
|
||||||
BookmarkBarControllerTest.HideWhenShowBookmarkBarFalse
|
|
||||||
|
|
||||||
# Crashes, see http://crbug.com/86656
|
|
||||||
MacSandboxTest.FileAccess
|
|
||||||
|
|
||||||
# http://crbug.com/87769
|
|
||||||
BalloonControllerTest.ShowAndCloseTest
|
|
||||||
BalloonControllerTest.SizesTest
|
|
||||||
|
|
||||||
# http://crbug.com/89030
|
|
||||||
ConnectionTesterTest.DeleteWhileInProgress
|
|
||||||
|
|
||||||
# http://crbug.com/93245
|
|
||||||
GeolocationWifiDataProviderCommonTest.*
|
|
||||||
|
|
||||||
# http://crbug.com/96298
|
|
||||||
FileSystemDirURLRequestJobTest.*
|
|
||||||
FileSystemURLRequestJobTest.*
|
|
||||||
FileSystemOperationWriteTest.*
|
|
|
@ -1,77 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# Prints a path to Valgrind binaries to be used for Chromium.
|
|
||||||
# Select the valgrind from third_party/valgrind by default,
|
|
||||||
# but allow users to override this default without editing scripts and
|
|
||||||
# without specifying a commandline option
|
|
||||||
|
|
||||||
export THISDIR=`dirname $0`
|
|
||||||
|
|
||||||
# User may use his own valgrind by giving its path with CHROME_VALGRIND env.
|
|
||||||
if [ "$CHROME_VALGRIND" = "" ]
|
|
||||||
then
|
|
||||||
# Guess which binaries we should use by uname
|
|
||||||
case "$(uname -a)" in
|
|
||||||
*Linux*x86_64*)
|
|
||||||
PLATFORM="linux_x64"
|
|
||||||
;;
|
|
||||||
*Linux*86*)
|
|
||||||
PLATFORM="linux_x86"
|
|
||||||
;;
|
|
||||||
*Darwin*9.[678].[01]*i386*)
|
|
||||||
# Didn't test other kernels.
|
|
||||||
PLATFORM="mac"
|
|
||||||
;;
|
|
||||||
*Darwin*10.[0-9].[0-9]*i386*)
|
|
||||||
PLATFORM="mac_10.6"
|
|
||||||
;;
|
|
||||||
*Darwin*10.[0-9].[0-9]*x86_64*)
|
|
||||||
PLATFORM="mac_10.6"
|
|
||||||
;;
|
|
||||||
*Darwin*11.[0-9].[0-9]*x86_64*)
|
|
||||||
PLATFORM="mac_10.7"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unknown platform:" >&2
|
|
||||||
uname -a >&2
|
|
||||||
echo "We'll try to search for valgrind binaries installed in /usr/local" >&2
|
|
||||||
PLATFORM=
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ "$PLATFORM" != "" ]
|
|
||||||
then
|
|
||||||
# The binaries should be in third_party/valgrind
|
|
||||||
# (checked out from deps/third_party/valgrind/binaries).
|
|
||||||
CHROME_VALGRIND="$THISDIR/../../third_party/valgrind/$PLATFORM"
|
|
||||||
|
|
||||||
# TODO(timurrrr): readlink -f is not present on Mac...
|
|
||||||
if [ "$PLATFORM" != "mac" ] && \
|
|
||||||
[ "$PLATFORM" != "mac_10.6" ] && \
|
|
||||||
[ "$PLATFORM" != "mac_10.7" ]
|
|
||||||
then
|
|
||||||
# Get rid of all "../" dirs
|
|
||||||
CHROME_VALGRIND=`readlink -f $CHROME_VALGRIND`
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! test -x $CHROME_VALGRIND/bin/valgrind
|
|
||||||
then
|
|
||||||
# We couldn't find the binaries in third_party/valgrind
|
|
||||||
CHROME_VALGRIND=""
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! test -x $CHROME_VALGRIND/bin/valgrind
|
|
||||||
then
|
|
||||||
echo "Oops, could not find Valgrind binaries in your checkout." >&2
|
|
||||||
echo "Please see" >&2
|
|
||||||
echo " http://dev.chromium.org/developers/how-tos/using-valgrind/get-valgrind" >&2
|
|
||||||
echo "for the instructions on how to download pre-built binaries." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo $CHROME_VALGRIND
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -1,81 +0,0 @@
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
"""
|
|
||||||
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
|
|
||||||
for more details on the presubmit API built into gcl.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
def CheckChange(input_api, output_api):
|
|
||||||
"""Checks the memcheck suppressions files for bad data."""
|
|
||||||
sup_regex = re.compile('suppressions.*\.txt$')
|
|
||||||
suppressions = {}
|
|
||||||
errors = []
|
|
||||||
check_for_memcheck = False
|
|
||||||
# skip_next_line has 3 possible values:
|
|
||||||
# - False: don't skip the next line.
|
|
||||||
# - 'skip_suppression_name': the next line is a suppression name, skip.
|
|
||||||
# - 'skip_param': the next line is a system call parameter error, skip.
|
|
||||||
skip_next_line = False
|
|
||||||
for f in filter(lambda x: sup_regex.search(x.LocalPath()),
|
|
||||||
input_api.AffectedFiles()):
|
|
||||||
for line, line_num in zip(f.NewContents(),
|
|
||||||
xrange(1, len(f.NewContents()) + 1)):
|
|
||||||
line = line.lstrip()
|
|
||||||
if line.startswith('#') or not line:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if skip_next_line:
|
|
||||||
if skip_next_line == 'skip_suppression_name':
|
|
||||||
if 'insert_a_suppression_name_here' in line:
|
|
||||||
errors.append('"insert_a_suppression_name_here" is not a valid '
|
|
||||||
'suppression name')
|
|
||||||
if suppressions.has_key(line):
|
|
||||||
if f.LocalPath() == suppressions[line][1]:
|
|
||||||
errors.append('suppression with name "%s" at %s line %s '
|
|
||||||
'has already been defined at line %s' %
|
|
||||||
(line, f.LocalPath(), line_num,
|
|
||||||
suppressions[line][1]))
|
|
||||||
else:
|
|
||||||
errors.append('suppression with name "%s" at %s line %s '
|
|
||||||
'has already been defined at %s line %s' %
|
|
||||||
(line, f.LocalPath(), line_num,
|
|
||||||
suppressions[line][0], suppressions[line][1]))
|
|
||||||
else:
|
|
||||||
suppressions[line] = (f, line_num)
|
|
||||||
check_for_memcheck = True;
|
|
||||||
skip_next_line = False
|
|
||||||
continue
|
|
||||||
if check_for_memcheck:
|
|
||||||
if not line.startswith('Memcheck:'):
|
|
||||||
errors.append('"%s" should be "Memcheck:..." in %s line %s' %
|
|
||||||
(line, f.LocalPath(), line_num))
|
|
||||||
check_for_memcheck = False;
|
|
||||||
if line == '{':
|
|
||||||
skip_next_line = 'skip_suppression_name'
|
|
||||||
continue
|
|
||||||
if line == "Memcheck:Param":
|
|
||||||
skip_next_line = 'skip_param'
|
|
||||||
continue
|
|
||||||
|
|
||||||
if (line.startswith('fun:') or line.startswith('obj:') or
|
|
||||||
line.startswith('Memcheck:') or line == '}' or
|
|
||||||
line == '...'):
|
|
||||||
continue
|
|
||||||
errors.append('"%s" is probably wrong: %s line %s' % (line, f.LocalPath(),
|
|
||||||
line_num))
|
|
||||||
if errors:
|
|
||||||
return [output_api.PresubmitError('\n'.join(errors))]
|
|
||||||
return []
|
|
||||||
|
|
||||||
def CheckChangeOnUpload(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
def CheckChangeOnCommit(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
def GetPreferredTrySlaves():
|
|
||||||
return ['linux_valgrind', 'mac_valgrind']
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,35 +0,0 @@
|
||||||
# There are three kinds of suppressions in this file:
|
|
||||||
# 1. Third party stuff we have no control over.
|
|
||||||
#
|
|
||||||
# 2. Intentional unit test errors, stuff that is somehow a false positive
|
|
||||||
# in our own code, or stuff that is so trivial it's not worth fixing.
|
|
||||||
#
|
|
||||||
# 3. Suppressions for real chromium bugs that are not yet fixed.
|
|
||||||
# These should all be in chromium's bug tracking system.
|
|
||||||
# Periodically we should sweep this file and the bug tracker clean by
|
|
||||||
# running overnight and removing outdated bugs/suppressions.
|
|
||||||
#
|
|
||||||
# TODO(rnk): Should we move all of the Linux-only system library suppressions
|
|
||||||
# over from suppressions.txt? We'd avoid wasting time parsing and matching
|
|
||||||
# suppressions on non-Linux, which is basically just Mac.
|
|
||||||
#
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
# 1. Third party stuff we have no control over.
|
|
||||||
{
|
|
||||||
# The InvalidRead error in rc4_wordconv is intentional.
|
|
||||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=341127
|
|
||||||
# TODO(wtc): This invalid read has been fixed in NSS 3.15. Remove this
|
|
||||||
# suppression when the system NSS libraries in Linux distributions are
|
|
||||||
# version 3.15 or later.
|
|
||||||
bug_43113 (Intentional)
|
|
||||||
Memcheck:Unaddressable
|
|
||||||
fun:rc4_wordconv
|
|
||||||
fun:RC4_Encrypt
|
|
||||||
}
|
|
||||||
|
|
||||||
# 2. Intentional unit test errors, stuff that is somehow a false positive
|
|
||||||
# in our own code, or stuff that is so trivial it's not worth fixing.
|
|
||||||
|
|
||||||
# 3. Suppressions for real chromium bugs that are not yet fixed.
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,634 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# memcheck_analyze.py
|
|
||||||
|
|
||||||
''' Given a valgrind XML file, parses errors and uniques them.'''
|
|
||||||
|
|
||||||
import gdb_helper
|
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
import hashlib
|
|
||||||
import logging
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from xml.dom.minidom import parse
|
|
||||||
from xml.parsers.expat import ExpatError
|
|
||||||
|
|
||||||
import common
|
|
||||||
|
|
||||||
# Global symbol table (yuck)
|
|
||||||
TheAddressTable = None
|
|
||||||
|
|
||||||
# These are regexps that define functions (using C++ mangled names)
|
|
||||||
# we don't want to see in stack traces while pretty printing
|
|
||||||
# or generating suppressions.
|
|
||||||
# Just stop printing the stack/suppression frames when the current one
|
|
||||||
# matches any of these.
|
|
||||||
_BORING_CALLERS = common.BoringCallers(mangled=True, use_re_wildcards=True)
|
|
||||||
|
|
||||||
def getTextOf(top_node, name):
|
|
||||||
''' Returns all text in all DOM nodes with a certain |name| that are children
|
|
||||||
of |top_node|.
|
|
||||||
'''
|
|
||||||
|
|
||||||
text = ""
|
|
||||||
for nodes_named in top_node.getElementsByTagName(name):
|
|
||||||
text += "".join([node.data for node in nodes_named.childNodes
|
|
||||||
if node.nodeType == node.TEXT_NODE])
|
|
||||||
return text
|
|
||||||
|
|
||||||
def getCDATAOf(top_node, name):
|
|
||||||
''' Returns all CDATA in all DOM nodes with a certain |name| that are children
|
|
||||||
of |top_node|.
|
|
||||||
'''
|
|
||||||
|
|
||||||
text = ""
|
|
||||||
for nodes_named in top_node.getElementsByTagName(name):
|
|
||||||
text += "".join([node.data for node in nodes_named.childNodes
|
|
||||||
if node.nodeType == node.CDATA_SECTION_NODE])
|
|
||||||
if (text == ""):
|
|
||||||
return None
|
|
||||||
return text
|
|
||||||
|
|
||||||
def shortenFilePath(source_dir, directory):
|
|
||||||
'''Returns a string with the string prefix |source_dir| removed from
|
|
||||||
|directory|.'''
|
|
||||||
prefixes_to_cut = ["build/src/", "valgrind/coregrind/", "out/Release/../../"]
|
|
||||||
|
|
||||||
if source_dir:
|
|
||||||
prefixes_to_cut.append(source_dir)
|
|
||||||
|
|
||||||
for p in prefixes_to_cut:
|
|
||||||
index = directory.rfind(p)
|
|
||||||
if index != -1:
|
|
||||||
directory = directory[index + len(p):]
|
|
||||||
|
|
||||||
return directory
|
|
||||||
|
|
||||||
# Constants that give real names to the abbreviations in valgrind XML output.
|
|
||||||
INSTRUCTION_POINTER = "ip"
|
|
||||||
OBJECT_FILE = "obj"
|
|
||||||
FUNCTION_NAME = "fn"
|
|
||||||
SRC_FILE_DIR = "dir"
|
|
||||||
SRC_FILE_NAME = "file"
|
|
||||||
SRC_LINE = "line"
|
|
||||||
|
|
||||||
def gatherFrames(node, source_dir):
|
|
||||||
frames = []
|
|
||||||
for frame in node.getElementsByTagName("frame"):
|
|
||||||
frame_dict = {
|
|
||||||
INSTRUCTION_POINTER : getTextOf(frame, INSTRUCTION_POINTER),
|
|
||||||
OBJECT_FILE : getTextOf(frame, OBJECT_FILE),
|
|
||||||
FUNCTION_NAME : getTextOf(frame, FUNCTION_NAME),
|
|
||||||
SRC_FILE_DIR : shortenFilePath(
|
|
||||||
source_dir, getTextOf(frame, SRC_FILE_DIR)),
|
|
||||||
SRC_FILE_NAME : getTextOf(frame, SRC_FILE_NAME),
|
|
||||||
SRC_LINE : getTextOf(frame, SRC_LINE)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Ignore this frame and all the following if it's a "boring" function.
|
|
||||||
enough_frames = False
|
|
||||||
for regexp in _BORING_CALLERS:
|
|
||||||
if re.match("^%s$" % regexp, frame_dict[FUNCTION_NAME]):
|
|
||||||
enough_frames = True
|
|
||||||
break
|
|
||||||
if enough_frames:
|
|
||||||
break
|
|
||||||
|
|
||||||
frames += [frame_dict]
|
|
||||||
|
|
||||||
global TheAddressTable
|
|
||||||
if TheAddressTable != None and frame_dict[SRC_LINE] == "":
|
|
||||||
# Try using gdb
|
|
||||||
TheAddressTable.Add(frame_dict[OBJECT_FILE],
|
|
||||||
frame_dict[INSTRUCTION_POINTER])
|
|
||||||
return frames
|
|
||||||
|
|
||||||
class ValgrindError:
|
|
||||||
''' Takes a <DOM Element: error> node and reads all the data from it. A
|
|
||||||
ValgrindError is immutable and is hashed on its pretty printed output.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, source_dir, error_node, commandline, testcase):
|
|
||||||
''' Copies all the relevant information out of the DOM and into object
|
|
||||||
properties.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
error_node: The <error></error> DOM node we're extracting from.
|
|
||||||
source_dir: Prefix that should be stripped from the <dir> node.
|
|
||||||
commandline: The command that was run under valgrind
|
|
||||||
testcase: The test case name, if known.
|
|
||||||
'''
|
|
||||||
|
|
||||||
# Valgrind errors contain one <what><stack> pair, plus an optional
|
|
||||||
# <auxwhat><stack> pair, plus an optional <origin><what><stack></origin>,
|
|
||||||
# plus (since 3.5.0) a <suppression></suppression> pair.
|
|
||||||
# (Origin is nicely enclosed; too bad the other two aren't.)
|
|
||||||
# The most common way to see all three in one report is
|
|
||||||
# a syscall with a parameter that points to uninitialized memory, e.g.
|
|
||||||
# Format:
|
|
||||||
# <error>
|
|
||||||
# <unique>0x6d</unique>
|
|
||||||
# <tid>1</tid>
|
|
||||||
# <kind>SyscallParam</kind>
|
|
||||||
# <what>Syscall param write(buf) points to uninitialised byte(s)</what>
|
|
||||||
# <stack>
|
|
||||||
# <frame>
|
|
||||||
# ...
|
|
||||||
# </frame>
|
|
||||||
# </stack>
|
|
||||||
# <auxwhat>Address 0x5c9af4f is 7 bytes inside a block of ...</auxwhat>
|
|
||||||
# <stack>
|
|
||||||
# <frame>
|
|
||||||
# ...
|
|
||||||
# </frame>
|
|
||||||
# </stack>
|
|
||||||
# <origin>
|
|
||||||
# <what>Uninitialised value was created by a heap allocation</what>
|
|
||||||
# <stack>
|
|
||||||
# <frame>
|
|
||||||
# ...
|
|
||||||
# </frame>
|
|
||||||
# </stack>
|
|
||||||
# </origin>
|
|
||||||
# <suppression>
|
|
||||||
# <sname>insert_a_suppression_name_here</sname>
|
|
||||||
# <skind>Memcheck:Param</skind>
|
|
||||||
# <skaux>write(buf)</skaux>
|
|
||||||
# <sframe> <fun>__write_nocancel</fun> </sframe>
|
|
||||||
# ...
|
|
||||||
# <sframe> <fun>main</fun> </sframe>
|
|
||||||
# <rawtext>
|
|
||||||
# <![CDATA[
|
|
||||||
# {
|
|
||||||
# <insert_a_suppression_name_here>
|
|
||||||
# Memcheck:Param
|
|
||||||
# write(buf)
|
|
||||||
# fun:__write_nocancel
|
|
||||||
# ...
|
|
||||||
# fun:main
|
|
||||||
# }
|
|
||||||
# ]]>
|
|
||||||
# </rawtext>
|
|
||||||
# </suppression>
|
|
||||||
# </error>
|
|
||||||
#
|
|
||||||
# Each frame looks like this:
|
|
||||||
# <frame>
|
|
||||||
# <ip>0x83751BC</ip>
|
|
||||||
# <obj>/data/dkegel/chrome-build/src/out/Release/base_unittests</obj>
|
|
||||||
# <fn>_ZN7testing8internal12TestInfoImpl7RunTestEPNS_8TestInfoE</fn>
|
|
||||||
# <dir>/data/dkegel/chrome-build/src/testing/gtest/src</dir>
|
|
||||||
# <file>gtest-internal-inl.h</file>
|
|
||||||
# <line>655</line>
|
|
||||||
# </frame>
|
|
||||||
# although the dir, file, and line elements are missing if there is
|
|
||||||
# no debug info.
|
|
||||||
|
|
||||||
self._kind = getTextOf(error_node, "kind")
|
|
||||||
self._backtraces = []
|
|
||||||
self._suppression = None
|
|
||||||
self._commandline = commandline
|
|
||||||
self._testcase = testcase
|
|
||||||
self._additional = []
|
|
||||||
|
|
||||||
# Iterate through the nodes, parsing <what|auxwhat><stack> pairs.
|
|
||||||
description = None
|
|
||||||
for node in error_node.childNodes:
|
|
||||||
if node.localName == "what" or node.localName == "auxwhat":
|
|
||||||
description = "".join([n.data for n in node.childNodes
|
|
||||||
if n.nodeType == n.TEXT_NODE])
|
|
||||||
elif node.localName == "xwhat":
|
|
||||||
description = getTextOf(node, "text")
|
|
||||||
elif node.localName == "stack":
|
|
||||||
assert description
|
|
||||||
self._backtraces.append([description, gatherFrames(node, source_dir)])
|
|
||||||
description = None
|
|
||||||
elif node.localName == "origin":
|
|
||||||
description = getTextOf(node, "what")
|
|
||||||
stack = node.getElementsByTagName("stack")[0]
|
|
||||||
frames = gatherFrames(stack, source_dir)
|
|
||||||
self._backtraces.append([description, frames])
|
|
||||||
description = None
|
|
||||||
stack = None
|
|
||||||
frames = None
|
|
||||||
elif description and node.localName != None:
|
|
||||||
# The lastest description has no stack, e.g. "Address 0x28 is unknown"
|
|
||||||
self._additional.append(description)
|
|
||||||
description = None
|
|
||||||
|
|
||||||
if node.localName == "suppression":
|
|
||||||
self._suppression = getCDATAOf(node, "rawtext");
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
''' Pretty print the type and backtrace(s) of this specific error,
|
|
||||||
including suppression (which is just a mangled backtrace).'''
|
|
||||||
output = ""
|
|
||||||
if (self._commandline):
|
|
||||||
output += self._commandline + "\n"
|
|
||||||
|
|
||||||
output += self._kind + "\n"
|
|
||||||
for backtrace in self._backtraces:
|
|
||||||
output += backtrace[0] + "\n"
|
|
||||||
filter = subprocess.Popen("c++filt -n", stdin=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT,
|
|
||||||
shell=True,
|
|
||||||
close_fds=True)
|
|
||||||
buf = ""
|
|
||||||
for frame in backtrace[1]:
|
|
||||||
buf += (frame[FUNCTION_NAME] or frame[INSTRUCTION_POINTER]) + "\n"
|
|
||||||
(stdoutbuf, stderrbuf) = filter.communicate(buf.encode('latin-1'))
|
|
||||||
demangled_names = stdoutbuf.split("\n")
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for frame in backtrace[1]:
|
|
||||||
output += (" " + demangled_names[i])
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
global TheAddressTable
|
|
||||||
if TheAddressTable != None and frame[SRC_FILE_DIR] == "":
|
|
||||||
# Try using gdb
|
|
||||||
foo = TheAddressTable.GetFileLine(frame[OBJECT_FILE],
|
|
||||||
frame[INSTRUCTION_POINTER])
|
|
||||||
if foo[0] != None:
|
|
||||||
output += (" (" + foo[0] + ":" + foo[1] + ")")
|
|
||||||
elif frame[SRC_FILE_DIR] != "":
|
|
||||||
output += (" (" + frame[SRC_FILE_DIR] + "/" + frame[SRC_FILE_NAME] +
|
|
||||||
":" + frame[SRC_LINE] + ")")
|
|
||||||
else:
|
|
||||||
output += " (" + frame[OBJECT_FILE] + ")"
|
|
||||||
output += "\n"
|
|
||||||
|
|
||||||
for additional in self._additional:
|
|
||||||
output += additional + "\n"
|
|
||||||
|
|
||||||
assert self._suppression != None, "Your Valgrind doesn't generate " \
|
|
||||||
"suppressions - is it too old?"
|
|
||||||
|
|
||||||
if self._testcase:
|
|
||||||
output += "The report came from the `%s` test.\n" % self._testcase
|
|
||||||
output += "Suppression (error hash=#%016X#):\n" % self.ErrorHash()
|
|
||||||
output += (" For more info on using suppressions see "
|
|
||||||
"http://dev.chromium.org/developers/tree-sheriffs/sheriff-details-chromium/memory-sheriff#TOC-Suppressing-memory-reports")
|
|
||||||
|
|
||||||
# Widen suppression slightly to make portable between mac and linux
|
|
||||||
# TODO(timurrrr): Oops, these transformations should happen
|
|
||||||
# BEFORE calculating the hash!
|
|
||||||
supp = self._suppression;
|
|
||||||
supp = supp.replace("fun:_Znwj", "fun:_Znw*")
|
|
||||||
supp = supp.replace("fun:_Znwm", "fun:_Znw*")
|
|
||||||
supp = supp.replace("fun:_Znaj", "fun:_Zna*")
|
|
||||||
supp = supp.replace("fun:_Znam", "fun:_Zna*")
|
|
||||||
|
|
||||||
# Make suppressions even less platform-dependent.
|
|
||||||
for sz in [1, 2, 4, 8]:
|
|
||||||
supp = supp.replace("Memcheck:Addr%d" % sz, "Memcheck:Unaddressable")
|
|
||||||
supp = supp.replace("Memcheck:Value%d" % sz, "Memcheck:Uninitialized")
|
|
||||||
supp = supp.replace("Memcheck:Cond", "Memcheck:Uninitialized")
|
|
||||||
|
|
||||||
# Split into lines so we can enforce length limits
|
|
||||||
supplines = supp.split("\n")
|
|
||||||
supp = None # to avoid re-use
|
|
||||||
|
|
||||||
# Truncate at line 26 (VG_MAX_SUPP_CALLERS plus 2 for name and type)
|
|
||||||
# or at the first 'boring' caller.
|
|
||||||
# (https://bugs.kde.org/show_bug.cgi?id=199468 proposes raising
|
|
||||||
# VG_MAX_SUPP_CALLERS, but we're probably fine with it as is.)
|
|
||||||
newlen = min(26, len(supplines));
|
|
||||||
|
|
||||||
# Drop boring frames and all the following.
|
|
||||||
enough_frames = False
|
|
||||||
for frameno in range(newlen):
|
|
||||||
for boring_caller in _BORING_CALLERS:
|
|
||||||
if re.match("^ +fun:%s$" % boring_caller, supplines[frameno]):
|
|
||||||
newlen = frameno
|
|
||||||
enough_frames = True
|
|
||||||
break
|
|
||||||
if enough_frames:
|
|
||||||
break
|
|
||||||
if (len(supplines) > newlen):
|
|
||||||
supplines = supplines[0:newlen]
|
|
||||||
supplines.append("}")
|
|
||||||
|
|
||||||
for frame in range(len(supplines)):
|
|
||||||
# Replace the always-changing anonymous namespace prefix with "*".
|
|
||||||
m = re.match("( +fun:)_ZN.*_GLOBAL__N_.*\.cc_" +
|
|
||||||
"[0-9a-fA-F]{8}_[0-9a-fA-F]{8}(.*)",
|
|
||||||
supplines[frame])
|
|
||||||
if m:
|
|
||||||
supplines[frame] = "*".join(m.groups())
|
|
||||||
|
|
||||||
output += "\n".join(supplines) + "\n"
|
|
||||||
|
|
||||||
return output
|
|
||||||
|
|
||||||
def UniqueString(self):
|
|
||||||
''' String to use for object identity. Don't print this, use str(obj)
|
|
||||||
instead.'''
|
|
||||||
rep = self._kind + " "
|
|
||||||
for backtrace in self._backtraces:
|
|
||||||
for frame in backtrace[1]:
|
|
||||||
rep += frame[FUNCTION_NAME]
|
|
||||||
|
|
||||||
if frame[SRC_FILE_DIR] != "":
|
|
||||||
rep += frame[SRC_FILE_DIR] + "/" + frame[SRC_FILE_NAME]
|
|
||||||
else:
|
|
||||||
rep += frame[OBJECT_FILE]
|
|
||||||
|
|
||||||
return rep
|
|
||||||
|
|
||||||
# This is a device-independent hash identifying the suppression.
|
|
||||||
# By printing out this hash we can find duplicate reports between tests and
|
|
||||||
# different shards running on multiple buildbots
|
|
||||||
def ErrorHash(self):
|
|
||||||
return int(hashlib.md5(self.UniqueString()).hexdigest()[:16], 16)
|
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.UniqueString())
|
|
||||||
def __eq__(self, rhs):
|
|
||||||
return self.UniqueString() == rhs
|
|
||||||
|
|
||||||
def log_is_finished(f, force_finish):
|
|
||||||
f.seek(0)
|
|
||||||
prev_line = ""
|
|
||||||
while True:
|
|
||||||
line = f.readline()
|
|
||||||
if line == "":
|
|
||||||
if not force_finish:
|
|
||||||
return False
|
|
||||||
# Okay, the log is not finished but we can make it up to be parseable:
|
|
||||||
if prev_line.strip() in ["</error>", "</errorcounts>", "</status>"]:
|
|
||||||
f.write("</valgrindoutput>\n")
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
if '</valgrindoutput>' in line:
|
|
||||||
# Valgrind often has garbage after </valgrindoutput> upon crash.
|
|
||||||
f.truncate()
|
|
||||||
return True
|
|
||||||
prev_line = line
|
|
||||||
|
|
||||||
class MemcheckAnalyzer:
|
|
||||||
''' Given a set of Valgrind XML files, parse all the errors out of them,
|
|
||||||
unique them and output the results.'''
|
|
||||||
|
|
||||||
SANITY_TEST_SUPPRESSIONS = {
|
|
||||||
"Memcheck sanity test 01 (memory leak).": 1,
|
|
||||||
"Memcheck sanity test 02 (malloc/read left).": 1,
|
|
||||||
"Memcheck sanity test 03 (malloc/read right).": 1,
|
|
||||||
"Memcheck sanity test 04 (malloc/write left).": 1,
|
|
||||||
"Memcheck sanity test 05 (malloc/write right).": 1,
|
|
||||||
"Memcheck sanity test 06 (new/read left).": 1,
|
|
||||||
"Memcheck sanity test 07 (new/read right).": 1,
|
|
||||||
"Memcheck sanity test 08 (new/write left).": 1,
|
|
||||||
"Memcheck sanity test 09 (new/write right).": 1,
|
|
||||||
"Memcheck sanity test 10 (write after free).": 1,
|
|
||||||
"Memcheck sanity test 11 (write after delete).": 1,
|
|
||||||
"Memcheck sanity test 12 (array deleted without []).": 1,
|
|
||||||
"Memcheck sanity test 13 (single element deleted with []).": 1,
|
|
||||||
"Memcheck sanity test 14 (malloc/read uninit).": 1,
|
|
||||||
"Memcheck sanity test 15 (new/read uninit).": 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Max time to wait for memcheck logs to complete.
|
|
||||||
LOG_COMPLETION_TIMEOUT = 180.0
|
|
||||||
|
|
||||||
def __init__(self, source_dir, show_all_leaks=False, use_gdb=False):
|
|
||||||
'''Create a parser for Memcheck logs.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
source_dir: Path to top of source tree for this build
|
|
||||||
show_all_leaks: Whether to show even less important leaks
|
|
||||||
use_gdb: Whether to use gdb to resolve source filenames and line numbers
|
|
||||||
in the report stacktraces
|
|
||||||
'''
|
|
||||||
self._source_dir = source_dir
|
|
||||||
self._show_all_leaks = show_all_leaks
|
|
||||||
self._use_gdb = use_gdb
|
|
||||||
|
|
||||||
# Contains the set of unique errors
|
|
||||||
self._errors = set()
|
|
||||||
|
|
||||||
# Contains the time when the we started analyzing the first log file.
|
|
||||||
# This variable is used to skip incomplete logs after some timeout.
|
|
||||||
self._analyze_start_time = None
|
|
||||||
|
|
||||||
|
|
||||||
def Report(self, files, testcase, check_sanity=False):
|
|
||||||
'''Reads in a set of files and prints Memcheck report.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
files: A list of filenames.
|
|
||||||
check_sanity: if true, search for SANITY_TEST_SUPPRESSIONS
|
|
||||||
'''
|
|
||||||
# Beyond the detailed errors parsed by ValgrindError above,
|
|
||||||
# the xml file contain records describing suppressions that were used:
|
|
||||||
# <suppcounts>
|
|
||||||
# <pair>
|
|
||||||
# <count>28</count>
|
|
||||||
# <name>pango_font_leak_todo</name>
|
|
||||||
# </pair>
|
|
||||||
# <pair>
|
|
||||||
# <count>378</count>
|
|
||||||
# <name>bug_13243</name>
|
|
||||||
# </pair>
|
|
||||||
# </suppcounts
|
|
||||||
# Collect these and print them at the end.
|
|
||||||
#
|
|
||||||
# With our patch for https://bugs.kde.org/show_bug.cgi?id=205000 in,
|
|
||||||
# the file also includes records of the form
|
|
||||||
# <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj>
|
|
||||||
# giving the filename and load address of each binary that was mapped
|
|
||||||
# into the process.
|
|
||||||
|
|
||||||
global TheAddressTable
|
|
||||||
if self._use_gdb:
|
|
||||||
TheAddressTable = gdb_helper.AddressTable()
|
|
||||||
else:
|
|
||||||
TheAddressTable = None
|
|
||||||
cur_report_errors = set()
|
|
||||||
suppcounts = defaultdict(int)
|
|
||||||
badfiles = set()
|
|
||||||
|
|
||||||
if self._analyze_start_time == None:
|
|
||||||
self._analyze_start_time = time.time()
|
|
||||||
start_time = self._analyze_start_time
|
|
||||||
|
|
||||||
parse_failed = False
|
|
||||||
for file in files:
|
|
||||||
# Wait up to three minutes for valgrind to finish writing all files,
|
|
||||||
# but after that, just skip incomplete files and warn.
|
|
||||||
f = open(file, "r+")
|
|
||||||
pid = re.match(".*\.([0-9]+)$", file)
|
|
||||||
if pid:
|
|
||||||
pid = pid.groups()[0]
|
|
||||||
found = False
|
|
||||||
running = True
|
|
||||||
firstrun = True
|
|
||||||
skip = False
|
|
||||||
origsize = os.path.getsize(file)
|
|
||||||
while (running and not found and not skip and
|
|
||||||
(firstrun or
|
|
||||||
((time.time() - start_time) < self.LOG_COMPLETION_TIMEOUT))):
|
|
||||||
firstrun = False
|
|
||||||
f.seek(0)
|
|
||||||
if pid:
|
|
||||||
# Make sure the process is still running so we don't wait for
|
|
||||||
# 3 minutes if it was killed. See http://crbug.com/17453
|
|
||||||
ps_out = subprocess.Popen("ps p %s" % pid, shell=True,
|
|
||||||
stdout=subprocess.PIPE).stdout
|
|
||||||
if len(ps_out.readlines()) < 2:
|
|
||||||
running = False
|
|
||||||
else:
|
|
||||||
skip = True
|
|
||||||
running = False
|
|
||||||
found = log_is_finished(f, False)
|
|
||||||
if not running and not found:
|
|
||||||
logging.warn("Valgrind process PID = %s is not running but its "
|
|
||||||
"XML log has not been finished correctly.\n"
|
|
||||||
"Make it up by adding some closing tags manually." % pid)
|
|
||||||
found = log_is_finished(f, not running)
|
|
||||||
if running and not found:
|
|
||||||
time.sleep(1)
|
|
||||||
f.close()
|
|
||||||
if not found:
|
|
||||||
badfiles.add(file)
|
|
||||||
else:
|
|
||||||
newsize = os.path.getsize(file)
|
|
||||||
if origsize > newsize+1:
|
|
||||||
logging.warn(str(origsize - newsize) +
|
|
||||||
" bytes of junk were after </valgrindoutput> in %s!" %
|
|
||||||
file)
|
|
||||||
try:
|
|
||||||
parsed_file = parse(file);
|
|
||||||
except ExpatError, e:
|
|
||||||
parse_failed = True
|
|
||||||
logging.warn("could not parse %s: %s" % (file, e))
|
|
||||||
lineno = e.lineno - 1
|
|
||||||
context_lines = 5
|
|
||||||
context_start = max(0, lineno - context_lines)
|
|
||||||
context_end = lineno + context_lines + 1
|
|
||||||
context_file = open(file, "r")
|
|
||||||
for i in range(0, context_start):
|
|
||||||
context_file.readline()
|
|
||||||
for i in range(context_start, context_end):
|
|
||||||
context_data = context_file.readline().rstrip()
|
|
||||||
if i != lineno:
|
|
||||||
logging.warn(" %s" % context_data)
|
|
||||||
else:
|
|
||||||
logging.warn("> %s" % context_data)
|
|
||||||
context_file.close()
|
|
||||||
continue
|
|
||||||
if TheAddressTable != None:
|
|
||||||
load_objs = parsed_file.getElementsByTagName("load_obj")
|
|
||||||
for load_obj in load_objs:
|
|
||||||
obj = getTextOf(load_obj, "obj")
|
|
||||||
ip = getTextOf(load_obj, "ip")
|
|
||||||
TheAddressTable.AddBinaryAt(obj, ip)
|
|
||||||
|
|
||||||
commandline = None
|
|
||||||
preamble = parsed_file.getElementsByTagName("preamble")[0];
|
|
||||||
for node in preamble.getElementsByTagName("line"):
|
|
||||||
if node.localName == "line":
|
|
||||||
for x in node.childNodes:
|
|
||||||
if x.nodeType == node.TEXT_NODE and "Command" in x.data:
|
|
||||||
commandline = x.data
|
|
||||||
break
|
|
||||||
|
|
||||||
raw_errors = parsed_file.getElementsByTagName("error")
|
|
||||||
for raw_error in raw_errors:
|
|
||||||
# Ignore "possible" leaks for now by default.
|
|
||||||
if (self._show_all_leaks or
|
|
||||||
getTextOf(raw_error, "kind") != "Leak_PossiblyLost"):
|
|
||||||
error = ValgrindError(self._source_dir,
|
|
||||||
raw_error, commandline, testcase)
|
|
||||||
if error not in cur_report_errors:
|
|
||||||
# We haven't seen such errors doing this report yet...
|
|
||||||
if error in self._errors:
|
|
||||||
# ... but we saw it in earlier reports, e.g. previous UI test
|
|
||||||
cur_report_errors.add("This error was already printed in "
|
|
||||||
"some other test, see 'hash=#%016X#'" % \
|
|
||||||
error.ErrorHash())
|
|
||||||
else:
|
|
||||||
# ... and we haven't seen it in other tests as well
|
|
||||||
self._errors.add(error)
|
|
||||||
cur_report_errors.add(error)
|
|
||||||
|
|
||||||
suppcountlist = parsed_file.getElementsByTagName("suppcounts")
|
|
||||||
if len(suppcountlist) > 0:
|
|
||||||
suppcountlist = suppcountlist[0]
|
|
||||||
for node in suppcountlist.getElementsByTagName("pair"):
|
|
||||||
count = getTextOf(node, "count");
|
|
||||||
name = getTextOf(node, "name");
|
|
||||||
suppcounts[name] += int(count)
|
|
||||||
|
|
||||||
if len(badfiles) > 0:
|
|
||||||
logging.warn("valgrind didn't finish writing %d files?!" % len(badfiles))
|
|
||||||
for file in badfiles:
|
|
||||||
logging.warn("Last 20 lines of %s :" % file)
|
|
||||||
os.system("tail -n 20 '%s' 1>&2" % file)
|
|
||||||
|
|
||||||
if parse_failed:
|
|
||||||
logging.error("FAIL! Couldn't parse Valgrind output file")
|
|
||||||
return -2
|
|
||||||
|
|
||||||
common.PrintUsedSuppressionsList(suppcounts)
|
|
||||||
|
|
||||||
retcode = 0
|
|
||||||
if cur_report_errors:
|
|
||||||
logging.error("FAIL! There were %s errors: " % len(cur_report_errors))
|
|
||||||
|
|
||||||
if TheAddressTable != None:
|
|
||||||
TheAddressTable.ResolveAll()
|
|
||||||
|
|
||||||
for error in cur_report_errors:
|
|
||||||
logging.error(error)
|
|
||||||
|
|
||||||
retcode = -1
|
|
||||||
|
|
||||||
# Report tool's insanity even if there were errors.
|
|
||||||
if check_sanity:
|
|
||||||
remaining_sanity_supp = MemcheckAnalyzer.SANITY_TEST_SUPPRESSIONS
|
|
||||||
for (name, count) in suppcounts.iteritems():
|
|
||||||
if (name in remaining_sanity_supp and
|
|
||||||
remaining_sanity_supp[name] == count):
|
|
||||||
del remaining_sanity_supp[name]
|
|
||||||
if remaining_sanity_supp:
|
|
||||||
logging.error("FAIL! Sanity check failed!")
|
|
||||||
logging.info("The following test errors were not handled: ")
|
|
||||||
for (name, count) in remaining_sanity_supp.iteritems():
|
|
||||||
logging.info(" * %dx %s" % (count, name))
|
|
||||||
retcode = -3
|
|
||||||
|
|
||||||
if retcode != 0:
|
|
||||||
return retcode
|
|
||||||
|
|
||||||
logging.info("PASS! No errors found!")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
|
||||||
'''For testing only. The MemcheckAnalyzer class should be imported instead.'''
|
|
||||||
parser = optparse.OptionParser("usage: %prog [options] <files to analyze>")
|
|
||||||
parser.add_option("", "--source_dir",
|
|
||||||
help="path to top of source tree for this build"
|
|
||||||
"(used to normalize source paths in baseline)")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
if len(args) == 0:
|
|
||||||
parser.error("no filename specified")
|
|
||||||
filenames = args
|
|
||||||
|
|
||||||
analyzer = MemcheckAnalyzer(options.source_dir, use_gdb=True)
|
|
||||||
return analyzer.Report(filenames, None)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(_main())
|
|
|
@ -1,138 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# Scape errors from the valgrind bots, reproduce them locally,
|
|
||||||
# save logs as regrind-TESTNAME.log, and display any errors found.
|
|
||||||
# Also save files regrind-failed.txt listing failed tests,
|
|
||||||
# and regrind-failed-map.txt showing which bot URLs have which failed tests
|
|
||||||
# (handy when filing bugs).
|
|
||||||
#
|
|
||||||
# Only scrapes linux layout bot at the moment.
|
|
||||||
# TODO: handle layout tests that don't have obvious path to test file
|
|
||||||
# TODO: extend script to handle more kinds of errors and more tests
|
|
||||||
|
|
||||||
# where the valgrind layout bot results live
|
|
||||||
LAYOUT_URL="http://build.chromium.org/p/chromium.memory.fyi/builders/Webkit%20Linux%20(valgrind%20layout)"
|
|
||||||
# how many builds back to check
|
|
||||||
LAYOUT_COUNT=250
|
|
||||||
|
|
||||||
# regexp to match valgrind errors
|
|
||||||
PATTERN="are definitely|uninitialised|Unhandled exception|\
|
|
||||||
Invalid read|Invalid write|Invalid free|Source and desti|Mismatched free|\
|
|
||||||
unaddressable byte|vex x86|the 'impossible' happened|\
|
|
||||||
valgrind:.*: Assertion.*failed|VALGRIND INTERNAL ERROR"
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
echo "Usage: regrind.sh [--noscrape][--norepro][--keep]"
|
|
||||||
echo "--noscrape: don't scrape bots, just use old regrind-failed.txt"
|
|
||||||
echo "--norepro: don't reproduce locally"
|
|
||||||
echo "--keep: keep temp files"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Given a log on stdin, list all the tests that failed in that log.
|
|
||||||
layout_list_failed_tests() {
|
|
||||||
grep "Command:.*LayoutTests" |
|
|
||||||
sed 's/<.*>//' |
|
|
||||||
sed 's/.*LayoutTests/LayoutTests/' |
|
|
||||||
sort -u |
|
|
||||||
tr -d '\015'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate a list of failed tests in regrind-failed.txt by scraping bot.
|
|
||||||
# Scrape most recent first, so if user interrupts, he is left with fresh-ish data.
|
|
||||||
scrape_layout() {
|
|
||||||
rm -f regrind-*.tmp* regrind-failed.txt regrind-failed-map.txt
|
|
||||||
touch regrind-failed.txt
|
|
||||||
|
|
||||||
# First, grab the number of the latest complete build.
|
|
||||||
wget -q -O regrind-builds.html "$LAYOUT_URL"
|
|
||||||
latest=`grep "<li><font .*" < regrind-builds.html | head -1 | sed 's/.*#//;s/<.*//'`
|
|
||||||
|
|
||||||
echo "Fetching $LAYOUT_COUNT logs from bot"
|
|
||||||
# Scrape the desired number of runs (150 is about one cycle)
|
|
||||||
first=`expr $latest - $LAYOUT_COUNT`
|
|
||||||
i=$latest
|
|
||||||
while test $i -ge $first
|
|
||||||
do
|
|
||||||
url="$LAYOUT_URL/builds/$i/steps/valgrind%20test:%20layout/logs/stdio"
|
|
||||||
wget -q -O regrind-$i.tmp "$url"
|
|
||||||
# Did any tests fail in this file?
|
|
||||||
layout_list_failed_tests < regrind-$i.tmp > regrind-$i.tmp.failed
|
|
||||||
if test -s regrind-$i.tmp.failed
|
|
||||||
then
|
|
||||||
# Yes. Log them to stdout,
|
|
||||||
echo "$url"
|
|
||||||
cat regrind-$i.tmp.failed
|
|
||||||
# to the table regrind-failed-map.txt,
|
|
||||||
cat regrind-$i.tmp.failed | sed "s,^,$url ," >> regrind-failed-map.txt
|
|
||||||
# and, if not already there, to regrind-failed.txt.
|
|
||||||
for test in `cat regrind-$i.tmp.failed`
|
|
||||||
do
|
|
||||||
fgrep "$test" regrind-failed.txt > /dev/null 2>&1 || echo "$test" >> regrind-failed.txt
|
|
||||||
done
|
|
||||||
else
|
|
||||||
rm regrind-$i.tmp.failed
|
|
||||||
fi
|
|
||||||
# Sleep 1/3 sec per fetch
|
|
||||||
case $i in
|
|
||||||
*[036]) sleep 1;;
|
|
||||||
esac
|
|
||||||
i=`expr $i - 1`
|
|
||||||
done
|
|
||||||
|
|
||||||
# Finally, munge the logs to identify tests that probably failed.
|
|
||||||
sh c.sh -l regrind-*.tmp > regrind-errfiles.txt
|
|
||||||
cat `cat regrind-errfiles.txt` | layout_list_failed_tests > regrind-failed.txt
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run the tests identified in regrind-failed.txt locally under valgrind.
|
|
||||||
# Save logs in regrind-$TESTNAME.log.
|
|
||||||
repro_layout() {
|
|
||||||
echo Running `wc -l < regrind-failed.txt` layout tests.
|
|
||||||
for test in `cat regrind-failed.txt`
|
|
||||||
do
|
|
||||||
logname="`echo $test | tr / _`"
|
|
||||||
echo "sh tools/valgrind/valgrind_webkit_tests.sh $test"
|
|
||||||
sh tools/valgrind/valgrind_webkit_tests.sh "$test" > regrind-"$logname".log 2>&1
|
|
||||||
egrep "$PATTERN" < regrind-"$logname".log | sed 's/==.*==//'
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
do_repro=1
|
|
||||||
do_scrape=1
|
|
||||||
do_cleanup=1
|
|
||||||
while test ! -z "$1"
|
|
||||||
do
|
|
||||||
case "$1" in
|
|
||||||
--noscrape) do_scrape=0;;
|
|
||||||
--norepro) do_repro=0;;
|
|
||||||
--keep) do_cleanup=0;;
|
|
||||||
*) usage;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "WARNING: This script is not supported and may be out of date"
|
|
||||||
|
|
||||||
if test $do_scrape = 0 && test $do_repro = 0
|
|
||||||
then
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $do_scrape = 1
|
|
||||||
then
|
|
||||||
scrape_layout
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $do_repro = 1
|
|
||||||
then
|
|
||||||
repro_layout
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $do_cleanup = 1
|
|
||||||
then
|
|
||||||
rm -f regrind-errfiles.txt regrind-*.tmp*
|
|
||||||
fi
|
|
|
@ -1,11 +0,0 @@
|
||||||
www.google.com
|
|
||||||
maps.google.com
|
|
||||||
news.google.com
|
|
||||||
www.youtube.com
|
|
||||||
build.chromium.org/p/chromium/waterfall
|
|
||||||
build.chromium.org/p/chromium.memory/console
|
|
||||||
build.chromium.org/f/chromium/perf/dashboard/overview.html
|
|
||||||
www.slashdot.org
|
|
||||||
www.ibanez.co.jp/japan/index.html
|
|
||||||
www.bbc.co.uk/arabic/
|
|
||||||
www.uni.edu/becker/chinese2.html
|
|
|
@ -1,227 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import errno
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import urllib
|
|
||||||
import urllib2
|
|
||||||
|
|
||||||
# Where all the data lives.
|
|
||||||
ROOT_URL = "http://build.chromium.org/p/chromium.memory.fyi/builders"
|
|
||||||
|
|
||||||
# TODO(groby) - support multi-line search from the command line. Useful when
|
|
||||||
# scanning for classes of failures, see below.
|
|
||||||
SEARCH_STRING = """<p class=\"failure result\">
|
|
||||||
Failed memory test: content
|
|
||||||
</p>"""
|
|
||||||
|
|
||||||
# Location of the log cache.
|
|
||||||
CACHE_DIR = "buildlogs.tmp"
|
|
||||||
|
|
||||||
# If we don't find anything after searching |CUTOFF| logs, we're probably done.
|
|
||||||
CUTOFF = 100
|
|
||||||
|
|
||||||
def EnsurePath(path):
|
|
||||||
"""Makes sure |path| does exist, tries to create it if it doesn't."""
|
|
||||||
try:
|
|
||||||
os.makedirs(path)
|
|
||||||
except OSError as exception:
|
|
||||||
if exception.errno != errno.EEXIST:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class Cache(object):
|
|
||||||
def __init__(self, root_dir):
|
|
||||||
self._root_dir = os.path.abspath(root_dir)
|
|
||||||
|
|
||||||
def _LocalName(self, name):
|
|
||||||
"""If name is a relative path, treat it as relative to cache root.
|
|
||||||
If it is absolute and under cache root, pass it through.
|
|
||||||
Otherwise, raise error.
|
|
||||||
"""
|
|
||||||
if os.path.isabs(name):
|
|
||||||
assert os.path.commonprefix([name, self._root_dir]) == self._root_dir
|
|
||||||
else:
|
|
||||||
name = os.path.join(self._root_dir, name)
|
|
||||||
return name
|
|
||||||
|
|
||||||
def _FetchLocal(self, local_name):
|
|
||||||
local_name = self._LocalName(local_name)
|
|
||||||
EnsurePath(os.path.dirname(local_name))
|
|
||||||
if os.path.exists(local_name):
|
|
||||||
f = open(local_name, 'r')
|
|
||||||
return f.readlines();
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _FetchRemote(self, remote_name):
|
|
||||||
try:
|
|
||||||
response = urllib2.urlopen(remote_name)
|
|
||||||
except:
|
|
||||||
print "Could not fetch", remote_name
|
|
||||||
raise
|
|
||||||
return response.read()
|
|
||||||
|
|
||||||
def Update(self, local_name, remote_name):
|
|
||||||
local_name = self._LocalName(local_name)
|
|
||||||
EnsurePath(os.path.dirname(local_name))
|
|
||||||
blob = self._FetchRemote(remote_name)
|
|
||||||
f = open(local_name, "w")
|
|
||||||
f.write(blob)
|
|
||||||
return blob.splitlines()
|
|
||||||
|
|
||||||
def FetchData(self, local_name, remote_name):
|
|
||||||
result = self._FetchLocal(local_name)
|
|
||||||
if result:
|
|
||||||
return result
|
|
||||||
# If we get here, the local cache does not exist yet. Fetch, and store.
|
|
||||||
return self.Update(local_name, remote_name)
|
|
||||||
|
|
||||||
|
|
||||||
class Builder(object):
|
|
||||||
def __init__(self, waterfall, name):
|
|
||||||
self._name = name
|
|
||||||
self._waterfall = waterfall
|
|
||||||
|
|
||||||
def Name(self):
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
def LatestBuild(self):
|
|
||||||
return self._waterfall.GetLatestBuild(self._name)
|
|
||||||
|
|
||||||
def GetBuildPath(self, build_num):
|
|
||||||
return "%s/%s/builds/%d" % (
|
|
||||||
self._waterfall._root_url, urllib.quote(self._name), build_num)
|
|
||||||
|
|
||||||
def _FetchBuildLog(self, build_num):
|
|
||||||
local_build_path = "builds/%s" % self._name
|
|
||||||
local_build_file = os.path.join(local_build_path, "%d.log" % build_num)
|
|
||||||
return self._waterfall._cache.FetchData(local_build_file,
|
|
||||||
self.GetBuildPath(build_num))
|
|
||||||
|
|
||||||
def _CheckLog(self, build_num, tester):
|
|
||||||
log_lines = self._FetchBuildLog(build_num)
|
|
||||||
return any(tester(line) for line in log_lines)
|
|
||||||
|
|
||||||
def ScanLogs(self, tester):
|
|
||||||
occurrences = []
|
|
||||||
build = self.LatestBuild()
|
|
||||||
no_results = 0
|
|
||||||
while build != 0 and no_results < CUTOFF:
|
|
||||||
if self._CheckLog(build, tester):
|
|
||||||
occurrences.append(build)
|
|
||||||
else:
|
|
||||||
no_results = no_results + 1
|
|
||||||
build = build - 1
|
|
||||||
return occurrences
|
|
||||||
|
|
||||||
|
|
||||||
class Waterfall(object):
|
|
||||||
def __init__(self, root_url, cache_dir):
|
|
||||||
self._root_url = root_url
|
|
||||||
self._builders = {}
|
|
||||||
self._top_revision = {}
|
|
||||||
self._cache = Cache(cache_dir)
|
|
||||||
|
|
||||||
def Builders(self):
|
|
||||||
return self._builders.values()
|
|
||||||
|
|
||||||
def Update(self):
|
|
||||||
self._cache.Update("builders", self._root_url)
|
|
||||||
self.FetchInfo()
|
|
||||||
|
|
||||||
def FetchInfo(self):
|
|
||||||
if self._top_revision:
|
|
||||||
return
|
|
||||||
|
|
||||||
html = self._cache.FetchData("builders", self._root_url)
|
|
||||||
|
|
||||||
""" Search for both builders and latest build number in HTML
|
|
||||||
<td class="box"><a href="builders/<builder-name>"> identifies a builder
|
|
||||||
<a href="builders/<builder-name>/builds/<build-num>"> is the latest build.
|
|
||||||
"""
|
|
||||||
box_matcher = re.compile('.*a href[^>]*>([^<]*)\<')
|
|
||||||
build_matcher = re.compile('.*a href=\"builders/(.*)/builds/([0-9]+)\".*')
|
|
||||||
last_builder = ""
|
|
||||||
for line in html:
|
|
||||||
if 'a href="builders/' in line:
|
|
||||||
if 'td class="box"' in line:
|
|
||||||
last_builder = box_matcher.match(line).group(1)
|
|
||||||
self._builders[last_builder] = Builder(self, last_builder)
|
|
||||||
else:
|
|
||||||
result = build_matcher.match(line)
|
|
||||||
builder = result.group(1)
|
|
||||||
assert builder == urllib.quote(last_builder)
|
|
||||||
self._top_revision[last_builder] = int(result.group(2))
|
|
||||||
|
|
||||||
def GetLatestBuild(self, name):
|
|
||||||
self.FetchInfo()
|
|
||||||
assert self._top_revision
|
|
||||||
return self._top_revision[name]
|
|
||||||
|
|
||||||
|
|
||||||
class MultiLineChange(object):
|
|
||||||
def __init__(self, lines):
|
|
||||||
self._tracked_lines = lines
|
|
||||||
self._current = 0
|
|
||||||
|
|
||||||
def __call__(self, line):
|
|
||||||
""" Test a single line against multi-line change.
|
|
||||||
|
|
||||||
If it matches the currently active line, advance one line.
|
|
||||||
If the current line is the last line, report a match.
|
|
||||||
"""
|
|
||||||
if self._tracked_lines[self._current] in line:
|
|
||||||
self._current = self._current + 1
|
|
||||||
if self._current == len(self._tracked_lines):
|
|
||||||
self._current = 0
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
self._current = 0
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
# Create argument parser.
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
commands = parser.add_mutually_exclusive_group(required=True)
|
|
||||||
commands.add_argument("--update", action='store_true')
|
|
||||||
commands.add_argument("--find", metavar='search term')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
path = os.path.abspath(os.path.dirname(argv[0]))
|
|
||||||
cache_path = os.path.join(path, CACHE_DIR)
|
|
||||||
|
|
||||||
fyi = Waterfall(ROOT_URL, cache_path)
|
|
||||||
|
|
||||||
if args.update:
|
|
||||||
fyi.Update()
|
|
||||||
for builder in fyi.Builders():
|
|
||||||
print "Updating", builder.Name()
|
|
||||||
builder.ScanLogs(lambda x:False)
|
|
||||||
|
|
||||||
if args.find:
|
|
||||||
tester = MultiLineChange(args.find.splitlines())
|
|
||||||
fyi.FetchInfo()
|
|
||||||
|
|
||||||
print "SCANNING FOR ", args.find
|
|
||||||
for builder in fyi.Builders():
|
|
||||||
print "Scanning", builder.Name()
|
|
||||||
occurrences = builder.ScanLogs(tester)
|
|
||||||
if occurrences:
|
|
||||||
min_build = min(occurrences)
|
|
||||||
path = builder.GetBuildPath(min_build)
|
|
||||||
print "Earliest occurrence in build %d" % min_build
|
|
||||||
print "Latest occurrence in build %d" % max(occurrences)
|
|
||||||
print "Latest build: %d" % builder.LatestBuild()
|
|
||||||
print path
|
|
||||||
print "%d total" % len(occurrences)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main(sys.argv))
|
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# Script to run tests under tools/valgrind/chrome_tests.sh
|
|
||||||
# in a loop looking for rare/flaky valgrind warnings, and
|
|
||||||
# generate suppressions for them, to be later filed as bugs
|
|
||||||
# and added to our suppressions file.
|
|
||||||
#
|
|
||||||
# FIXME: Layout tests are a bit funny - they have their own
|
|
||||||
# sharding control, and should probably be tweaked to obey
|
|
||||||
# GTEST_SHARD_INDEX/GTEST_TOTAL_SHARDS like the rest,
|
|
||||||
# but they take days and days to run, so they are left
|
|
||||||
# out of this script.
|
|
||||||
|
|
||||||
if test ! -d chrome
|
|
||||||
then
|
|
||||||
echo "Please run from parent directory of chrome and build directories"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test "$1" = ""
|
|
||||||
then
|
|
||||||
echo "Usage: shard-all-tests.sh [BUILDTYPE=Release] target [target ...]"
|
|
||||||
echo "Example: shard-all-tests.sh ui_tests"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -x
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Regexp to match any valgrind error
|
|
||||||
PATTERN="ERROR SUMMARY: [^0]|are definitely|uninitialised|Unhandled exception|\
|
|
||||||
Invalid read|Invalid write|Invalid free|Source and desti|Mismatched free|\
|
|
||||||
unaddressable byte|vex x86|impossible|Assertion|INTERNAL ERROR|finish writing|OUCH"
|
|
||||||
|
|
||||||
BUILDTYPE=Debug
|
|
||||||
case "$1" in
|
|
||||||
BUILDTYPE=Debug) BUILDTYPE=Debug ; shift ;;
|
|
||||||
BUILDTYPE=Release) BUILDTYPE=Release ; shift ;;
|
|
||||||
BUILDTYPE=*) echo "unknown build type $1"; exit 1;;
|
|
||||||
*) ;;
|
|
||||||
esac
|
|
||||||
TESTS="$@"
|
|
||||||
|
|
||||||
what_to_build() {
|
|
||||||
echo $TESTS | tr ' ' '\012' | grep -v layout_tests || true
|
|
||||||
echo $TESTS | grep -q layout_tests && echo test_shell || true
|
|
||||||
echo $TESTS | grep -q ui_tests && echo chrome || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wrap xcodebuild to take same arguments as our make, more or less
|
|
||||||
xcodemake() {
|
|
||||||
for target in $*
|
|
||||||
do
|
|
||||||
case $target in
|
|
||||||
chrome) xcodebuild -configuration $BUILDTYPE -project chrome/chrome.xcodeproj -target chrome ;;
|
|
||||||
ui_tests) xcodebuild -configuration $BUILDTYPE -project chrome/chrome.xcodeproj -target ui_tests ;;
|
|
||||||
base_unittests) xcodebuild -configuration $BUILDTYPE -project base/base.xcodeproj -target base_unittests ;;
|
|
||||||
net_unittests) xcodebuild -configuration $BUILDTYPE -project net/net.xcodeproj -target net_unittests ;;
|
|
||||||
*) echo "dunno how to build $target yet"; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
build_tests() {
|
|
||||||
buildtype=$1
|
|
||||||
shift
|
|
||||||
|
|
||||||
OS=`uname`
|
|
||||||
case $OS in
|
|
||||||
Linux)
|
|
||||||
# Lame way to autodetect whether 'make' or 'hammer' is in use
|
|
||||||
if test -d out
|
|
||||||
then
|
|
||||||
make -j4 BUILDTYPE=$1 $@
|
|
||||||
else
|
|
||||||
# fixme: obey buildtype
|
|
||||||
hammer $@
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
Darwin)
|
|
||||||
xcodemake $@
|
|
||||||
;;
|
|
||||||
*) echo "don't know how to build on os $OS"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
TESTS_BUILDABLE=`what_to_build`
|
|
||||||
echo building $TESTS_BUILDABLE
|
|
||||||
build_tests $BUILDTYPE $TESTS_BUILDABLE
|
|
||||||
|
|
||||||
# Divide each test suite up into 100 shards, as first step
|
|
||||||
# in tracking down exact source of errors.
|
|
||||||
export GTEST_TOTAL_SHARDS=100
|
|
||||||
|
|
||||||
rm -rf *.vlog *.vtmp || true
|
|
||||||
|
|
||||||
iter=0
|
|
||||||
while test $iter -lt 1000
|
|
||||||
do
|
|
||||||
for testname in $TESTS
|
|
||||||
do
|
|
||||||
export GTEST_SHARD_INDEX=0
|
|
||||||
while test $GTEST_SHARD_INDEX -lt $GTEST_TOTAL_SHARDS
|
|
||||||
do
|
|
||||||
i=$GTEST_SHARD_INDEX
|
|
||||||
sh tools/valgrind/chrome_tests.sh -b xcodebuild/$BUILDTYPE -t ${testname} --tool_flags="--nocleanup_on_exit" > ${testname}_$i.vlog 2>&1 || true
|
|
||||||
mv valgrind.tmp ${testname}_$i.vtmp
|
|
||||||
GTEST_SHARD_INDEX=`expr $GTEST_SHARD_INDEX + 1`
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# Save any interesting log files from this iteration
|
|
||||||
# Also show interesting lines on stdout, to make tail -f more interesting
|
|
||||||
if egrep "$PATTERN" *.vlog
|
|
||||||
then
|
|
||||||
mkdir -p shard-results/$iter
|
|
||||||
mv `egrep -l "$PATTERN" *.vlog` shard-results/$iter
|
|
||||||
# ideally we'd only save the .vtmp's corresponding to the .vlogs we saved
|
|
||||||
mv *.vtmp shard-results/$iter
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf *.vlog *.vtmp || true
|
|
||||||
iter=`expr $iter + 1`
|
|
||||||
done
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,177 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
from collections import defaultdict
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import suppressions
|
|
||||||
|
|
||||||
|
|
||||||
def ReadReportsFromFile(filename):
|
|
||||||
""" Returns a list of (report_hash, report) and the URL of the report on the
|
|
||||||
waterfall.
|
|
||||||
"""
|
|
||||||
input_file = file(filename, 'r')
|
|
||||||
# reports is a list of (error hash, report) pairs.
|
|
||||||
reports = []
|
|
||||||
in_suppression = False
|
|
||||||
cur_supp = []
|
|
||||||
# This stores the last error hash found while reading the file.
|
|
||||||
last_hash = ""
|
|
||||||
for line in input_file:
|
|
||||||
line = line.strip()
|
|
||||||
line = line.replace("</span><span class=\"stdout\">", "")
|
|
||||||
line = line.replace("</span><span class=\"stderr\">", "")
|
|
||||||
line = line.replace("<", "<")
|
|
||||||
line = line.replace(">", ">")
|
|
||||||
if in_suppression:
|
|
||||||
if line == "}":
|
|
||||||
cur_supp += ["}"]
|
|
||||||
reports += [[last_hash, "\n".join(cur_supp)]]
|
|
||||||
in_suppression = False
|
|
||||||
cur_supp = []
|
|
||||||
last_hash = ""
|
|
||||||
else:
|
|
||||||
cur_supp += [" "*3 + line]
|
|
||||||
elif line == "{":
|
|
||||||
in_suppression = True
|
|
||||||
cur_supp = ["{"]
|
|
||||||
elif line.find("Suppression (error hash=#") == 0:
|
|
||||||
last_hash = line[25:41]
|
|
||||||
# The line at the end of the file is assumed to store the URL of the report.
|
|
||||||
return reports,line
|
|
||||||
|
|
||||||
def Demangle(names):
|
|
||||||
""" Demangle a list of C++ symbols, return a list of human-readable symbols.
|
|
||||||
"""
|
|
||||||
args = ['c++filt', '-n']
|
|
||||||
args.extend(names)
|
|
||||||
pipe = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
|
||||||
stdout, _ = pipe.communicate()
|
|
||||||
demangled = stdout.split("\n")
|
|
||||||
|
|
||||||
# Each line ends with a newline, so the final entry of the split output
|
|
||||||
# will always be ''.
|
|
||||||
assert len(demangled) == len(names) + 1
|
|
||||||
return demangled[:-1]
|
|
||||||
|
|
||||||
def GetSymbolsFromReport(report):
|
|
||||||
"""Extract all symbols from a suppression report."""
|
|
||||||
symbols = []
|
|
||||||
prefix = "fun:"
|
|
||||||
prefix_len = len(prefix)
|
|
||||||
for line in report.splitlines():
|
|
||||||
index = line.find(prefix)
|
|
||||||
if index != -1:
|
|
||||||
symbols.append(line[index + prefix_len:])
|
|
||||||
return symbols
|
|
||||||
|
|
||||||
def PrintTopSymbols(symbol_reports, top_count):
|
|
||||||
"""Print the |top_count| symbols with the most occurrences."""
|
|
||||||
boring_symbols=['malloc', '_Znw*', 'TestBody']
|
|
||||||
sorted_reports = sorted(filter(lambda x:x[0] not in boring_symbols,
|
|
||||||
symbol_reports.iteritems()),
|
|
||||||
key=lambda x:len(x[1]), reverse=True)
|
|
||||||
symbols = symbol_reports.keys()
|
|
||||||
demangled = Demangle(symbols)
|
|
||||||
assert len(demangled) == len(symbols)
|
|
||||||
symboltable = dict(zip(symbols, demangled))
|
|
||||||
|
|
||||||
print "\n"
|
|
||||||
print "Top %d symbols" % top_count
|
|
||||||
for (symbol, suppressions) in sorted_reports[:top_count]:
|
|
||||||
print "%4d occurrences : %s" % (len(suppressions), symboltable[symbol])
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
supp = suppressions.GetSuppressions()
|
|
||||||
|
|
||||||
# all_reports is a map {report: list of urls containing this report}
|
|
||||||
all_reports = defaultdict(list)
|
|
||||||
report_hashes = {}
|
|
||||||
symbol_reports = defaultdict(list)
|
|
||||||
|
|
||||||
# Create argument parser.
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument('--top-symbols', type=int, default=0,
|
|
||||||
help='Print a list of the top <n> symbols')
|
|
||||||
parser.add_argument('--symbol-filter', action='append',
|
|
||||||
help='Filter out all suppressions not containing the specified symbol(s). '
|
|
||||||
'Matches against the mangled names')
|
|
||||||
|
|
||||||
parser.add_argument('reports', metavar='report file', nargs='+',
|
|
||||||
help='List of report files')
|
|
||||||
args = parser.parse_args(argv)
|
|
||||||
|
|
||||||
for f in args.reports:
|
|
||||||
f_reports, url = ReadReportsFromFile(f)
|
|
||||||
for (hash, report) in f_reports:
|
|
||||||
all_reports[report] += [url]
|
|
||||||
report_hashes[report] = hash
|
|
||||||
|
|
||||||
reports_count = 0
|
|
||||||
for r in all_reports:
|
|
||||||
cur_supp = supp['common_suppressions']
|
|
||||||
if all([re.search("%20Mac%20|mac_valgrind", url)
|
|
||||||
for url in all_reports[r]]):
|
|
||||||
# Include mac suppressions if the report is only present on Mac
|
|
||||||
cur_supp += supp['mac_suppressions']
|
|
||||||
elif all([re.search("Windows%20", url) for url in all_reports[r]]):
|
|
||||||
# Include win32 suppressions if the report is only present on Windows
|
|
||||||
cur_supp += supp['win_suppressions']
|
|
||||||
elif all([re.search("Linux%20", url) for url in all_reports[r]]):
|
|
||||||
cur_supp += supp['linux_suppressions']
|
|
||||||
elif all([re.search("%20Heapcheck", url)
|
|
||||||
for url in all_reports[r]]):
|
|
||||||
cur_supp += supp['heapcheck_suppressions']
|
|
||||||
if all(["DrMemory" in url for url in all_reports[r]]):
|
|
||||||
cur_supp += supp['drmem_suppressions']
|
|
||||||
if all(["DrMemory%20full" in url for url in all_reports[r]]):
|
|
||||||
cur_supp += supp['drmem_full_suppressions']
|
|
||||||
|
|
||||||
# Test if this report is already suppressed
|
|
||||||
skip = False
|
|
||||||
for s in cur_supp:
|
|
||||||
if s.Match(r.split("\n")):
|
|
||||||
skip = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# Skip reports if none of the symbols are in the report.
|
|
||||||
if args.symbol_filter and all(not s in r for s in args.symbol_filter):
|
|
||||||
skip = True
|
|
||||||
|
|
||||||
if not skip:
|
|
||||||
reports_count += 1
|
|
||||||
print "==================================="
|
|
||||||
print "This report observed at"
|
|
||||||
for url in all_reports[r]:
|
|
||||||
print " %s" % url
|
|
||||||
print "didn't match any suppressions:"
|
|
||||||
print "Suppression (error hash=#%s#):" % (report_hashes[r])
|
|
||||||
print r
|
|
||||||
print "==================================="
|
|
||||||
|
|
||||||
if args.top_symbols > 0:
|
|
||||||
symbols = GetSymbolsFromReport(r)
|
|
||||||
for symbol in symbols:
|
|
||||||
symbol_reports[symbol].append(report_hashes[r])
|
|
||||||
|
|
||||||
if reports_count > 0:
|
|
||||||
print ("%d unique reports don't match any of the suppressions" %
|
|
||||||
reports_count)
|
|
||||||
if args.top_symbols > 0:
|
|
||||||
PrintTopSymbols(symbol_reports, args.top_symbols)
|
|
||||||
|
|
||||||
else:
|
|
||||||
print "Congratulations! All reports are suppressed!"
|
|
||||||
# TODO(timurrrr): also make sure none of the old suppressions
|
|
||||||
# were narrowed too much.
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main(sys.argv[1:])
|
|
|
@ -1 +0,0 @@
|
||||||
*
|
|
|
@ -1,35 +0,0 @@
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
"""
|
|
||||||
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
|
|
||||||
for more details on the presubmit API built into gcl.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChange(input_api, output_api):
|
|
||||||
"""Checks the TSan suppressions files for bad suppressions."""
|
|
||||||
|
|
||||||
# TODO(timurrrr): find out how to do relative imports
|
|
||||||
# and remove this ugly hack. Also, the CheckChange function won't be needed.
|
|
||||||
tools_vg_path = input_api.os_path.join(input_api.PresubmitLocalPath(), '..')
|
|
||||||
import sys
|
|
||||||
old_path = sys.path
|
|
||||||
try:
|
|
||||||
sys.path = sys.path + [tools_vg_path]
|
|
||||||
import suppressions
|
|
||||||
return suppressions.PresubmitCheck(input_api, output_api)
|
|
||||||
finally:
|
|
||||||
sys.path = old_path
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChangeOnUpload(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
|
|
||||||
def CheckChangeOnCommit(input_api, output_api):
|
|
||||||
return CheckChange(input_api, output_api)
|
|
||||||
|
|
||||||
|
|
||||||
def GetPreferredTrySlaves():
|
|
||||||
return ['linux_tsan']
|
|
|
@ -1,188 +0,0 @@
|
||||||
# This file lists the functions, object files and source files
|
|
||||||
# which should be ignored (i.e. not instrumented) by ThreadSanitizer.
|
|
||||||
# See http://code.google.com/p/data-race-test/wiki/ThreadSanitizerIgnores.
|
|
||||||
|
|
||||||
# ignore these libraries
|
|
||||||
obj:*/libfreetype*
|
|
||||||
obj:*/libdbus*
|
|
||||||
|
|
||||||
# we ignore the whole NSS library for now since
|
|
||||||
# its instrumentation is very slow.
|
|
||||||
# TODO(timurrrr): investigate whether we need to instrument it
|
|
||||||
obj:*/libnss*
|
|
||||||
obj:*/nss/*
|
|
||||||
|
|
||||||
# ignore pulseaudio - We don't have symbols there and it can be slow otherwise
|
|
||||||
obj:*/libpulse*.so*
|
|
||||||
|
|
||||||
# ignore this standard stuff
|
|
||||||
fun:clone
|
|
||||||
fun:fork
|
|
||||||
fun:pthread_*
|
|
||||||
fun_r:_pthread_exit
|
|
||||||
fun_r:_pthread_free_pthread_onstack
|
|
||||||
fun_r:random_r
|
|
||||||
fun_r:random
|
|
||||||
fun_r:rand
|
|
||||||
fun_r:srand
|
|
||||||
fun:__new_exitfn
|
|
||||||
fun:_dl_*
|
|
||||||
fun:__dl_*
|
|
||||||
fun:*_setjmp*
|
|
||||||
|
|
||||||
# dark magic with 'errno' here.
|
|
||||||
fun:sys_*
|
|
||||||
|
|
||||||
# ignore libc's printf functions
|
|
||||||
fun_r:_IO_*
|
|
||||||
fun:fwrite
|
|
||||||
fun:fflush
|
|
||||||
|
|
||||||
# False reports on std::string internals, see
|
|
||||||
# http://code.google.com/p/data-race-test/issues/detail?id=40
|
|
||||||
fun:*_M_mutateE*
|
|
||||||
fun_r:*_M_set_length_and_sharable*
|
|
||||||
fun:*_M_is_leaked*
|
|
||||||
fun:*_M_is_shared*
|
|
||||||
fun:*_M_set_leaked*
|
|
||||||
fun:*_M_set_sharable*
|
|
||||||
|
|
||||||
# Comparison of std::strings sometimes takes a lot of time but we don't really
|
|
||||||
# need precise stack traces there.
|
|
||||||
fun_hist:_ZStltIcSt11char_traitsIcESaIcEEbRKSbIT_T0_T1_ES8_
|
|
||||||
fun_hist:_ZNKSs7compareERKSs
|
|
||||||
|
|
||||||
# Don't instrument intercepts
|
|
||||||
src:*ts_valgrind_intercepts.c
|
|
||||||
|
|
||||||
##################################################################
|
|
||||||
# Don't instrument synchronization code
|
|
||||||
src:*base/threading/thread_local_storage*
|
|
||||||
src:*base/stats_counters*
|
|
||||||
src:*base/synchronization/condition_variable*
|
|
||||||
src:*base/synchronization/lock*
|
|
||||||
src:*base/synchronization/waitable_event*
|
|
||||||
|
|
||||||
# Don't instrument code dealing with atomics (base::subtle)
|
|
||||||
fun:*base*subtle*Release_Store*
|
|
||||||
fun:*base*subtle*NoBarrier_CompareAndSwap*
|
|
||||||
fun:*base*subtle*NoBarrier_Load*
|
|
||||||
# Keep some mangling so we don't match NoBarrier_AtomicIncrement
|
|
||||||
fun:*base*subtle23Barrier_AtomicIncrement*
|
|
||||||
|
|
||||||
# MD5 computations are very slow due since sums are computed by
|
|
||||||
# repeatedly calling tiny functions and is unlikely to race with
|
|
||||||
# anything.
|
|
||||||
src:*base/md5*
|
|
||||||
|
|
||||||
# Don't instrument tcmalloc
|
|
||||||
src:*/tcmalloc/*
|
|
||||||
|
|
||||||
# This function is heavy in net_unittests
|
|
||||||
fun_r:*disk_cache*BackendImpl*CheckAllEntries*
|
|
||||||
|
|
||||||
# V8 is a hot-spot under ThreadSanitizer.
|
|
||||||
# Lots of tiny functions there...
|
|
||||||
# TODO(timurrrr):
|
|
||||||
# Can we miss data races on V8 objects due to non thread-safe API calls
|
|
||||||
# if we don't instrument v8::internals?
|
|
||||||
fun_r:*v8*internal*
|
|
||||||
|
|
||||||
# unibrow namespace contains lots of tiny unicode conversion functions.
|
|
||||||
fun_hist:*unibrow*
|
|
||||||
|
|
||||||
# Histogram has tiny functions that can be called frequently
|
|
||||||
fun_hist:*Histogram*
|
|
||||||
# Recursively ignore Histrogram::Add and friends, see http://crbug.com/62694.
|
|
||||||
fun_r:*4base*9Histogram*3Add*
|
|
||||||
fun_r:*4base*16HistogramSamples*3Add*
|
|
||||||
fun_r:*4base*13HistogramBase*7AddTime*
|
|
||||||
|
|
||||||
# TODO(timurrrr): SKIA - needs separate testing?
|
|
||||||
# SKIA unittest is single-threaded...
|
|
||||||
# SKIA uses un-annotated atomic refcount and other sync stuff
|
|
||||||
# some functions are HEAVY like png, jpeg decoding
|
|
||||||
src:*third_party/skia*
|
|
||||||
|
|
||||||
# WebKit hotspot
|
|
||||||
fun:*png_write*
|
|
||||||
|
|
||||||
# This function generates 25% of memory accesses in net_unittests
|
|
||||||
fun:*icu_4_2*UnicodeSet*add*
|
|
||||||
|
|
||||||
# SQLite has lots of tiny functions and produce too many segments on some tests.
|
|
||||||
# See http://crbug.com/56511
|
|
||||||
fun_hist:*sqlite*
|
|
||||||
|
|
||||||
# There's some weird failure test going on in this tiny test function in sqlite
|
|
||||||
fun_r:threadLockingTest
|
|
||||||
|
|
||||||
# Ignore accesses below GetCurrentThreadIdentifier.
|
|
||||||
# There is a benign race which is hard to suppress properly,
|
|
||||||
# see http://crbug.com/44580
|
|
||||||
fun_r:*BrowserThread*GetCurrentThreadIdentifier*
|
|
||||||
|
|
||||||
# BrowserThread accesses MessageLoop::current() in ::CurrentlyOn.
|
|
||||||
# We can't use suppressions to hide these reports since the concurrent stack
|
|
||||||
# is simply "base::Thread::ThreadMain"
|
|
||||||
# See http://crbug.com/63678
|
|
||||||
fun_r:*BrowserThread*CurrentlyOn*
|
|
||||||
|
|
||||||
# zlib is smarter than we are, see http://www.zlib.net/zlib_faq.html#faq36
|
|
||||||
fun_r:inflate
|
|
||||||
# zlib-related reports, not investigated yet. See http://crbug.com/70932
|
|
||||||
fun_r:*remoting*CompressorZlib*Process*
|
|
||||||
|
|
||||||
# X11 reads the _XErrorFunction callback in a racey way, see
|
|
||||||
# http://crbug.com/65278
|
|
||||||
fun:XSetErrorHandler
|
|
||||||
|
|
||||||
fun:*IPC*Logging*Enable*
|
|
||||||
fun:*IPC*Logging*Disable*
|
|
||||||
|
|
||||||
# TSan doesn't support lockf and hence shared memory locks in this function;
|
|
||||||
# http://crbug.com/45083
|
|
||||||
fun_r:*base*StatsTable*AddCounter*
|
|
||||||
|
|
||||||
# TSan doesn't understand internal libc locks, see http://crbug.com/71435
|
|
||||||
fun_r:mbsrtowcs
|
|
||||||
|
|
||||||
# gethostbyname2_r is thread-safe, however ThreadSanitizer reports races inside it and
|
|
||||||
# (sometimes) in __nss_* functions below it.
|
|
||||||
# This may be related to
|
|
||||||
# https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/59449
|
|
||||||
fun_r:gethostbyname2_r*
|
|
||||||
|
|
||||||
# TODO(timurrrr): remove this when TSan is updated past r3232
|
|
||||||
fun_r:gaih_inet
|
|
||||||
|
|
||||||
# Strange reports below _IO_getline, every time in "Concurrent access".
|
|
||||||
# Probably the reports are there since we're missing the libc internal locks
|
|
||||||
fun_r:_IO_getline*
|
|
||||||
|
|
||||||
# A benign race in glib on something called "contention_counter".
|
|
||||||
fun:g_slice_alloc
|
|
||||||
|
|
||||||
# A benign race in glibc on "random_time_bits".
|
|
||||||
fun:__gen_tempname
|
|
||||||
|
|
||||||
# A probably-benign race on '__have_o_cloexec' in opendir/__alloc_dir,
|
|
||||||
# see http://crbug.com/125928.
|
|
||||||
fun_r:__alloc_dir
|
|
||||||
fun_r:opendir
|
|
||||||
|
|
||||||
# The sqlite cache is racing against a few different stacktraces,
|
|
||||||
# so let's ignore it recursively. See http://crbug.com/84094
|
|
||||||
fun_r:pcache1Fetch
|
|
||||||
|
|
||||||
# "Suppress" a data race in TraceLog::GetCategory which has
|
|
||||||
# fun:MessageLoop::RunTask at the top of the "current" stack which we don't want
|
|
||||||
# to suppress. See http://crbug.com/98926
|
|
||||||
fun:*base*debug*TraceLog*GetCategoryInternal*
|
|
||||||
|
|
||||||
# libc threading on GCC 4.6
|
|
||||||
fun:arena_thread_freeres
|
|
||||||
|
|
||||||
# __strncasecmp_l_ssse3 overreads the buffer causing TSan to report a data race
|
|
||||||
# on another object. See http://crbug.com/177074
|
|
||||||
fun:*strncasecmp*
|
|
|
@ -1,38 +0,0 @@
|
||||||
# This file lists the functions, object files and source files
|
|
||||||
# which should be ignored (i.e. not instrumented) by ThreadSanitizer on Mac OS.
|
|
||||||
# At the moment the Chromium binaries' debug info is not available to
|
|
||||||
# ThreadSanitizer, so we have to define fun:* rules for Mac OS complementing
|
|
||||||
# the src:* rules defined for Linux.
|
|
||||||
|
|
||||||
# we ignore the Security libraries for now since
|
|
||||||
# their instrumentation is very slow.
|
|
||||||
# TODO(timurrrr): investigate whether we need to instrument them
|
|
||||||
obj:*/Security*
|
|
||||||
obj:*/libcrypto*
|
|
||||||
# SensitiveAllocator::free is a part of the Security framework.
|
|
||||||
# It calls bzero (0xffff0633) which can't be resolved and thus should be
|
|
||||||
# ignored recursively.
|
|
||||||
fun_r:*SensitiveAllocator*free*
|
|
||||||
|
|
||||||
# The CFBag and CFDictionary operators should be thread-safe, but they are not
|
|
||||||
# annotated properly.
|
|
||||||
# TODO(glider): replace all the CoreFoundation suppressions with ignores.
|
|
||||||
fun_r:CFBag*
|
|
||||||
fun_r:CFDictionary*
|
|
||||||
fun_r:CFBasicDictionary*
|
|
||||||
#fun_r:CFBasicHash*
|
|
||||||
|
|
||||||
# see crbug.com/46138
|
|
||||||
fun_r:__CFRunLoopDeallocate
|
|
||||||
|
|
||||||
fun_r:__CFRunLoopRemoveAllSources
|
|
||||||
fun_r:__CFFinalizeRunLoop
|
|
||||||
|
|
||||||
# _cthread_fork_child() is called in the child process after the fork syscall.
|
|
||||||
# This function cleans up the cthread data structures created in the parent,
|
|
||||||
# so ThreadSanitizer might consider it racey.
|
|
||||||
fun_r:_cthread_fork_child
|
|
||||||
|
|
||||||
# False reports on Snow Leopard.
|
|
||||||
fun_r: _pthread_exit
|
|
||||||
fun_r: _dispatch_queue_drain
|
|
|
@ -1,64 +0,0 @@
|
||||||
# This file lists the functions, object files and source files
|
|
||||||
# which should be ignored (i.e. not instrumented) by ThreadSanitizer on Windows.
|
|
||||||
|
|
||||||
# We ignore security libraries for now since their instrumentation is very slow.
|
|
||||||
# TODO(timurrrr): investigate whether we need to instrument them
|
|
||||||
obj:*CRYPT32.dll
|
|
||||||
obj:*RPCRT4.dll
|
|
||||||
fun_r:*SHA256*
|
|
||||||
fun_r:*BCryptGenerateSymmetricKey*
|
|
||||||
fun_r:*CryptAcquireContext*
|
|
||||||
|
|
||||||
obj:*WINHTTP.dll
|
|
||||||
obj:*imagehlp.dll
|
|
||||||
|
|
||||||
# Instrumenting IP Helper API causes crashes.
|
|
||||||
# TODO(szym): investigate http://crbug.com/146119
|
|
||||||
obj:*IPHLPAPI.dll
|
|
||||||
|
|
||||||
# Use less detailed instrumentation of STL
|
|
||||||
fun_hist:*std::*<*
|
|
||||||
# Don't instrument some stl internals - they shouldn't be useful
|
|
||||||
fun_r:*std::_Debug*
|
|
||||||
fun_r:*std::_Lockit*
|
|
||||||
|
|
||||||
# Benign race on mutex unlock
|
|
||||||
fun:_Mtxunlock
|
|
||||||
|
|
||||||
# Benign race during clock initialization
|
|
||||||
fun_r:*InitializeClock*
|
|
||||||
|
|
||||||
# Some unknown Windows guts
|
|
||||||
fun_r:Ordinal_*
|
|
||||||
fun:unnamedImageEntryPoint
|
|
||||||
fun_r:RtlDestroyQueryDebugBuffer
|
|
||||||
fun:_updatetlocinfoEx_nolock
|
|
||||||
|
|
||||||
# Strange reports on net_unittests, maybe related to raising
|
|
||||||
# a debug exception by PlatformThread
|
|
||||||
# TODO(timurrrr): investigate
|
|
||||||
fun_r:*PlatformThread*SetName*
|
|
||||||
|
|
||||||
# Recursively ignore Histrogram::Add and friends, see http://crbug.com/62694.
|
|
||||||
fun_r:base::Histogram::Add
|
|
||||||
fun_r:base::HistogramSamples::Add
|
|
||||||
fun_r:base::HistogramBase::AddTime
|
|
||||||
|
|
||||||
# ffmpegsumo.dll appears to read a few bytes beyond the end of the buffer.
|
|
||||||
fun:_ff_prefetch_mmxext
|
|
||||||
|
|
||||||
# Shows up as a race in SHELL32.dll when deleting a directory while opening an
|
|
||||||
# unrelated file in another thread. Revealed by DiskCacheBackendTest.DeleteOld.
|
|
||||||
# See: https://code.google.com/p/data-race-test/issues/detail?id=114
|
|
||||||
fun_r:SHFileOperationW
|
|
||||||
|
|
||||||
# Ignore internal file I/O synchronization: crbug.com/146724
|
|
||||||
fun_r:_lock_file
|
|
||||||
fun_r:_lock_file2
|
|
||||||
fun_r:_lock
|
|
||||||
fun_r:_flsbuf
|
|
||||||
fun_r:_unlock_file
|
|
||||||
fun_r:_getstream
|
|
||||||
|
|
||||||
# http://crbug.com/272065
|
|
||||||
obj:*NLAapi.dll
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,270 +0,0 @@
|
||||||
# There are two kinds of suppressions in this file.
|
|
||||||
# 1. third party stuff we have no control over
|
|
||||||
#
|
|
||||||
# 2. Intentional unit test errors, or stuff that is somehow a false positive
|
|
||||||
# in our own code, or stuff that is so trivial it's not worth fixing
|
|
||||||
#
|
|
||||||
# 3. Suppressions for real chromium bugs that are not yet fixed.
|
|
||||||
# These should all be in chromium's bug tracking system (but a few aren't yet).
|
|
||||||
# Periodically we should sweep this file and the bug tracker clean by
|
|
||||||
# running overnight and removing outdated bugs/suppressions.
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
|
|
||||||
# 1. third party stuff we have no control over
|
|
||||||
# Several Cocoa-specific races
|
|
||||||
{
|
|
||||||
Some Cocoa-specific race in NSRunLoop class
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*CFRunLoop*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A race releasing NSObject
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:__CFDoExternRefOperation
|
|
||||||
fun:-[NSObject release]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A race retaining NSObject
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:CFBagAddValue
|
|
||||||
fun:__CFDoExternRefOperation
|
|
||||||
fun:-[NSObject retain]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A race retaining NSBundle
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:CFBagAddValue
|
|
||||||
fun:__CFDoExternRefOperation
|
|
||||||
fun:NSIncrementExtraRefCount
|
|
||||||
fun:-[NSBundle retain]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A race deallocating NSOperationQueue
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:_CFRelease
|
|
||||||
fun:-[NSOperationQueue dealloc]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Another race deallocating NSOperationQueue
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:-[NSIndexSet dealloc]
|
|
||||||
fun:-[NSOperationQueue dealloc]
|
|
||||||
}
|
|
||||||
|
|
||||||
# A related OpenRadar bug is at http://openradar.appspot.com/7396501.
|
|
||||||
{
|
|
||||||
A benign race on a debug counter in __NSAutoreleaseObject
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:__NSAutoreleaseObject
|
|
||||||
fun:-[NSObject(NSObject) autorelease]
|
|
||||||
}
|
|
||||||
|
|
||||||
# media_unittests depend on the Darwin libraries which have many reports in
|
|
||||||
# them. A related OpenRadar bug is at http://openradar.appspot.com/7223948
|
|
||||||
{
|
|
||||||
Warnings in the CoreAudio component
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
obj:/System/Library/Components/CoreAudio.component*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Warnings in the CoreAudio framework
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
obj:/System/Library/Frameworks/CoreAudio.framework*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A warning in CoreAudio framework
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*HALRunLoop*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A warning in the AudioToolbox framework
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*CAPThread*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Warnings inside AQServer_{Stop,EnqueueBuffer}
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*AudioQueueObject*
|
|
||||||
...
|
|
||||||
fun:AQServer_*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Warnings inside AudioHardwareGetProperty
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:AudioHardwareGetProperty
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Benign data race in CAMutex bug_23579
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:*CAMutex*ock*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
A warning on destruction of third party ClientAudioQueue object (AudioToolbox)
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*AQClient*CheckDisposal*
|
|
||||||
fun:*ClientAudioQueueD*
|
|
||||||
fun:AudioQueueDispose
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Destroying invalid lock in krb5int_getaddrinfo while terminating Kerberos.
|
|
||||||
ThreadSanitizer:InvalidLock
|
|
||||||
fun:pthread_mutex_destroy
|
|
||||||
fun:krb5int_getaddrinfo
|
|
||||||
fun:ImageLoaderMachO::doTermination*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_55946
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:OSAtomicAdd32
|
|
||||||
fun:base::subtle::Barrier_AtomicIncrement*
|
|
||||||
}
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
# 2. Intentional unit test errors, or stuff that is somehow a false positive
|
|
||||||
# in our own code, or stuff that is so trivial it's not worth fixing
|
|
||||||
|
|
||||||
{
|
|
||||||
Benign data race inside PCMQueueOutAudioOutputStream::Stop bug_24801
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:*PCMQueueOutAudioOutputStream*Stop*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_100313 TSan false positive
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:__sfp
|
|
||||||
fun:fopen
|
|
||||||
fun:file_util::OpenFile
|
|
||||||
fun:base::SharedMemory::CreateNamed
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Benign race to access status during TrackedObject unittests
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:tracked_objects::ThreadData::ShutdownSingleThreadedCleanup
|
|
||||||
}
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------
|
|
||||||
# 3. Suppressions for real chromium bugs that are not yet fixed.
|
|
||||||
# These should all be in chromium's bug tracking system (but a few aren't yet).
|
|
||||||
# Periodically we should sweep this file and the bug tracker clean by
|
|
||||||
# running overnight and removing outdated bugs/suppressions.
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_93932j
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:release_delayed_buffers
|
|
||||||
fun:frame_thread_free
|
|
||||||
fun:ff_thread_free
|
|
||||||
fun:avcodec_close
|
|
||||||
fun:avcodec_close
|
|
||||||
fun:media::FFmpegVideoDecoder::ReleaseFFmpegResources
|
|
||||||
fun:media::FFmpegVideoDecoder::Stop
|
|
||||||
fun:base::internal::RunnableAdapter::Run
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_100772a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:CAGuard::Wait
|
|
||||||
fun:MIO::DAL::RunLoop::StartOwnThread
|
|
||||||
fun:MIO::DAL::RunLoop::Start
|
|
||||||
fun:MIO::DAL::System::CheckOutInstance
|
|
||||||
fun:TundraObjectGetPropertyDataSize
|
|
||||||
fun:+[QTCaptureDALDevice _refreshDevices]
|
|
||||||
fun:+[QTCaptureDALDevice devicesWithIOType:]
|
|
||||||
fun:+[QTCaptureDevice devicesWithIOType:]
|
|
||||||
fun:+[QTCaptureDevice inputDevices]
|
|
||||||
fun:+[QTCaptureDevice inputDevicesWithMediaType:]
|
|
||||||
fun:+[VideoCaptureDeviceQTKit deviceNames]
|
|
||||||
fun:media::VideoCaptureDevice::GetDeviceNames
|
|
||||||
fun:media::VideoCaptureDeviceMac::Init
|
|
||||||
fun:media::VideoCaptureDevice::Create
|
|
||||||
fun:media::VideoCaptureDeviceTest_OpenInvalidDevice_Test::TestBody
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_100772b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:DVDeviceTerminate
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_100772c
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:MIO::DAL::RunLoop::StopOwnThread
|
|
||||||
fun:MIO::DAL::RunLoop::Teardown
|
|
||||||
fun:MIO::DAL::System::TeardownShell
|
|
||||||
fun:MIO::DAL::System::AtExitHandler
|
|
||||||
fun:MIO::DAL::AtExit::AtExitHandler
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_100772d
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:DVSignalSync
|
|
||||||
fun:DVDeviceTerminate
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_106197
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:__sfp
|
|
||||||
fun:fopen
|
|
||||||
fun:file_util::OpenFile
|
|
||||||
fun:base::SharedMemory::Create
|
|
||||||
fun:base::SharedMemory::CreateNamed
|
|
||||||
fun:base::::MultipleThreadMain::ThreadMain
|
|
||||||
fun:base::::ThreadFunc
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_123112
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:media::AUAudioOutputStream::Stop
|
|
||||||
fun:media::AudioOutputDispatcherImpl::StopStream
|
|
||||||
fun:media::AudioOutputProxy::Stop
|
|
||||||
fun:media::AudioOutputController::DoStopCloseAndClearStream
|
|
||||||
fun:media::AudioOutputController::DoClose
|
|
||||||
fun:base::internal::RunnableAdapter::Run
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_133074_a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:CAMutex::~CAMutex
|
|
||||||
fun:AudioQueueDispose
|
|
||||||
fun:media::PCMQueueOutAudioOutputStream::Close
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_133074_b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:media::AUAudioOutputStream::Stop
|
|
||||||
fun:media::AudioOutputMixer::ClosePhysicalStream
|
|
||||||
fun:media::AudioOutputMixer::Shutdown
|
|
||||||
fun:media::AudioManagerBase::ShutdownOnAudioThread
|
|
||||||
}
|
|
|
@ -1,225 +0,0 @@
|
||||||
############################
|
|
||||||
# Reports on the guts of Windows
|
|
||||||
{
|
|
||||||
UuidCreate
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:UuidCreate
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
ILFindLastID
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:ILFindLastID
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
RpcServerUnregisterIf
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:RpcServerUnregisterIf
|
|
||||||
}
|
|
||||||
|
|
||||||
# http://code.google.com/p/data-race-test/issues/detail?id=45
|
|
||||||
{
|
|
||||||
accessing an invalid lock in unnamedImageEntryPoint
|
|
||||||
ThreadSanitizer:InvalidLock
|
|
||||||
fun:unnamedImageEntryPoint
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
accessing an invalid lock in CoFreeAllLibraries
|
|
||||||
ThreadSanitizer:InvalidLock
|
|
||||||
fun:CoFreeAllLibraries
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_158099_mmdevice_endpoint_shutdown_too_fast
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:GetLocalIdFromEndpointId
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
############################
|
|
||||||
# Chromium
|
|
||||||
|
|
||||||
{
|
|
||||||
Benign race durung InitializeClock
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:*InitializeClock*
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_62560
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:_initterm
|
|
||||||
fun:doexit
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
accessing an invalid lock under exit/doexit
|
|
||||||
ThreadSanitizer:InvalidLock
|
|
||||||
fun:*~Lock*
|
|
||||||
...
|
|
||||||
fun:doexit
|
|
||||||
fun:exit
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_81793a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:NetTestSuite::InitializeTestThread
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_81793b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:base::MessageLoop::CalculateDelayedRuntime
|
|
||||||
fun:base::MessageLoop::Post*Task
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:avcodec_default_release_buffer
|
|
||||||
fun:ff_mpeg4video_split
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:avcodec_close
|
|
||||||
fun:media::FFmpegVideoDecoder::ReleaseFFmpegResources
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932d
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:memcpy
|
|
||||||
fun:media::CopyPlane
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932e
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:ff_thread_finish_setup
|
|
||||||
fun:ptw32_threadStart@4
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932f
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:ff_vp3_h_loop_filter_c
|
|
||||||
...
|
|
||||||
fun:ff_thread_flush
|
|
||||||
fun:media::FFmpegVideoDecoder::Flush
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932g
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:av_parser_close
|
|
||||||
...
|
|
||||||
fun:BaseThreadInitThunk
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932h
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:av_parser_close
|
|
||||||
...
|
|
||||||
fun:base::internal::RunnableAdapter::Run
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_93932i
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:ff_simple_idct_add_mmx
|
|
||||||
...
|
|
||||||
fun:BaseThreadInitThunk
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_144928_a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:google_breakpad::CrashGenerationServer::Handle*
|
|
||||||
fun:google_breakpad::CrashGenerationServer::OnPipeConnected
|
|
||||||
fun:RtlSetTimer
|
|
||||||
fun:RtlSetTimer
|
|
||||||
fun:TpReleaseTimer
|
|
||||||
fun:TpReleaseTimer
|
|
||||||
fun:RtlMultiByteToUnicodeSize
|
|
||||||
fun:TpCallbackMayRunLong
|
|
||||||
fun:TpCallbackMayRunLong
|
|
||||||
fun:BaseThreadInitThunk
|
|
||||||
}
|
|
||||||
{
|
|
||||||
bug_144928_b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:google_breakpad::CrashGenerationServer::~CrashGenerationServer
|
|
||||||
fun:google_breakpad::CrashGenerationServer::`scalar deleting destructor'
|
|
||||||
fun:base::DefaultDeleter*
|
|
||||||
fun:base::internal::scoped_ptr_impl::~scoped_ptr_impl
|
|
||||||
fun:remoting::BreakpadWinDeathTest::~BreakpadWinDeathTest
|
|
||||||
fun:remoting::BreakpadWinDeathTest_TestAccessViolation_Test::`scalar deleting destructor'
|
|
||||||
fun:testing::Test::DeleteSelf_
|
|
||||||
fun:testing::internal::HandleExceptionsInMethodIfSupported
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_146119
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:GetAdaptersAddresses
|
|
||||||
...
|
|
||||||
fun:base::internal::RunnableAdapter::Run
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_157076_a
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:win32thread_worker
|
|
||||||
fun:_callthreadstartex
|
|
||||||
fun:_threadstartex
|
|
||||||
fun:BaseThreadInitThunk
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_157076_b
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:memset
|
|
||||||
fun:_free_dbg_nolock
|
|
||||||
fun:_free_dbg
|
|
||||||
fun:_aligned_free_dbg
|
|
||||||
fun:_aligned_free
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_157076_c
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:memset
|
|
||||||
fun:_heap_alloc_dbg_impl
|
|
||||||
fun:_nh_malloc_dbg_impl
|
|
||||||
fun:_nh_malloc_dbg
|
|
||||||
fun:_malloc_dbg
|
|
||||||
fun:_aligned_offset_malloc_dbg
|
|
||||||
fun:_aligned_malloc
|
|
||||||
fun:base::AlignedAlloc
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_170334
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:net::NetworkChangeNotifierWinTest::~NetworkChangeNotifierWinTest
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
bug_239350
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
...
|
|
||||||
fun:av_freep
|
|
||||||
fun:av_buffer_unref
|
|
||||||
fun:av_frame_unref
|
|
||||||
...
|
|
||||||
fun:media::FFmpegVideoDecoder::Decode
|
|
||||||
}
|
|
|
@ -1,278 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# tsan_analyze.py
|
|
||||||
|
|
||||||
''' Given a ThreadSanitizer output file, parses errors and uniques them.'''
|
|
||||||
|
|
||||||
import gdb_helper
|
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
import hashlib
|
|
||||||
import logging
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
import common
|
|
||||||
|
|
||||||
# Global symbol table (ugh)
|
|
||||||
TheAddressTable = None
|
|
||||||
|
|
||||||
class _StackTraceLine(object):
|
|
||||||
def __init__(self, line, address, binary):
|
|
||||||
self.raw_line_ = line
|
|
||||||
self.address = address
|
|
||||||
self.binary = binary
|
|
||||||
def __str__(self):
|
|
||||||
global TheAddressTable
|
|
||||||
file, line = TheAddressTable.GetFileLine(self.binary, self.address)
|
|
||||||
if (file is None) or (line is None):
|
|
||||||
return self.raw_line_
|
|
||||||
else:
|
|
||||||
return self.raw_line_.replace(self.binary, '%s:%s' % (file, line))
|
|
||||||
|
|
||||||
class TsanAnalyzer(object):
|
|
||||||
''' Given a set of ThreadSanitizer output files, parse all the errors out of
|
|
||||||
them, unique them and output the results.'''
|
|
||||||
|
|
||||||
LOAD_LIB_RE = re.compile('--[0-9]+-- ([^(:]*) \((0x[0-9a-f]+)\)')
|
|
||||||
TSAN_LINE_RE = re.compile('==[0-9]+==\s*[#0-9]+\s*'
|
|
||||||
'([0-9A-Fa-fx]+):'
|
|
||||||
'(?:[^ ]* )*'
|
|
||||||
'([^ :\n]+)'
|
|
||||||
'')
|
|
||||||
THREAD_CREATION_STR = ("INFO: T.* "
|
|
||||||
"(has been created by T.* at this point|is program's main thread)")
|
|
||||||
|
|
||||||
SANITY_TEST_SUPPRESSION = ("ThreadSanitizer sanity test "
|
|
||||||
"(ToolsSanityTest.DataRace)")
|
|
||||||
TSAN_RACE_DESCRIPTION = "Possible data race"
|
|
||||||
TSAN_WARNING_DESCRIPTION = ("Unlocking a non-locked lock"
|
|
||||||
"|accessing an invalid lock"
|
|
||||||
"|which did not acquire this lock")
|
|
||||||
RACE_VERIFIER_LINE = "Confirmed a race|unexpected race"
|
|
||||||
TSAN_ASSERTION = "Assertion failed: "
|
|
||||||
|
|
||||||
def __init__(self, source_dir, use_gdb=False):
|
|
||||||
'''Reads in a set of files.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
source_dir: Path to top of source tree for this build
|
|
||||||
'''
|
|
||||||
|
|
||||||
self._use_gdb = use_gdb
|
|
||||||
self._cur_testcase = None
|
|
||||||
|
|
||||||
def ReadLine(self):
|
|
||||||
self.line_ = self.cur_fd_.readline()
|
|
||||||
self.stack_trace_line_ = None
|
|
||||||
if not self._use_gdb:
|
|
||||||
return
|
|
||||||
global TheAddressTable
|
|
||||||
match = TsanAnalyzer.LOAD_LIB_RE.match(self.line_)
|
|
||||||
if match:
|
|
||||||
binary, ip = match.groups()
|
|
||||||
TheAddressTable.AddBinaryAt(binary, ip)
|
|
||||||
return
|
|
||||||
match = TsanAnalyzer.TSAN_LINE_RE.match(self.line_)
|
|
||||||
if match:
|
|
||||||
address, binary_name = match.groups()
|
|
||||||
stack_trace_line = _StackTraceLine(self.line_, address, binary_name)
|
|
||||||
TheAddressTable.Add(stack_trace_line.binary, stack_trace_line.address)
|
|
||||||
self.stack_trace_line_ = stack_trace_line
|
|
||||||
|
|
||||||
def ReadSection(self):
|
|
||||||
""" Example of a section:
|
|
||||||
==4528== WARNING: Possible data race: {{{
|
|
||||||
==4528== T20 (L{}):
|
|
||||||
==4528== #0 MyTest::Foo1
|
|
||||||
==4528== #1 MyThread::ThreadBody
|
|
||||||
==4528== Concurrent write happened at this point:
|
|
||||||
==4528== T19 (L{}):
|
|
||||||
==4528== #0 MyTest::Foo2
|
|
||||||
==4528== #1 MyThread::ThreadBody
|
|
||||||
==4528== }}}
|
|
||||||
------- suppression -------
|
|
||||||
{
|
|
||||||
<Put your suppression name here>
|
|
||||||
ThreadSanitizer:Race
|
|
||||||
fun:MyTest::Foo1
|
|
||||||
fun:MyThread::ThreadBody
|
|
||||||
}
|
|
||||||
------- end suppression -------
|
|
||||||
"""
|
|
||||||
result = [self.line_]
|
|
||||||
if re.search("{{{", self.line_):
|
|
||||||
while not re.search('}}}', self.line_):
|
|
||||||
self.ReadLine()
|
|
||||||
if self.stack_trace_line_ is None:
|
|
||||||
result.append(self.line_)
|
|
||||||
else:
|
|
||||||
result.append(self.stack_trace_line_)
|
|
||||||
self.ReadLine()
|
|
||||||
if re.match('-+ suppression -+', self.line_):
|
|
||||||
# We need to calculate the suppression hash and prepend a line like
|
|
||||||
# "Suppression (error hash=#0123456789ABCDEF#):" so the buildbot can
|
|
||||||
# extract the suppression snippet.
|
|
||||||
supp = ""
|
|
||||||
while not re.match('-+ end suppression -+', self.line_):
|
|
||||||
self.ReadLine()
|
|
||||||
supp += self.line_
|
|
||||||
self.ReadLine()
|
|
||||||
if self._cur_testcase:
|
|
||||||
result.append("The report came from the `%s` test.\n" % \
|
|
||||||
self._cur_testcase)
|
|
||||||
result.append("Suppression (error hash=#%016X#):\n" % \
|
|
||||||
(int(hashlib.md5(supp).hexdigest()[:16], 16)))
|
|
||||||
result.append(" For more info on using suppressions see "
|
|
||||||
"http://dev.chromium.org/developers/how-tos/using-valgrind/threadsanitizer#TOC-Suppressing-data-races\n")
|
|
||||||
result.append(supp)
|
|
||||||
else:
|
|
||||||
self.ReadLine()
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def ReadTillTheEnd(self):
|
|
||||||
result = [self.line_]
|
|
||||||
while self.line_:
|
|
||||||
self.ReadLine()
|
|
||||||
result.append(self.line_)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def ParseReportFile(self, filename):
|
|
||||||
'''Parses a report file and returns a list of ThreadSanitizer reports.
|
|
||||||
|
|
||||||
|
|
||||||
Args:
|
|
||||||
filename: report filename.
|
|
||||||
Returns:
|
|
||||||
list of (list of (str iff self._use_gdb, _StackTraceLine otherwise)).
|
|
||||||
'''
|
|
||||||
ret = []
|
|
||||||
self.cur_fd_ = open(filename, 'r')
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# Read ThreadSanitizer reports.
|
|
||||||
self.ReadLine()
|
|
||||||
if not self.line_:
|
|
||||||
break
|
|
||||||
|
|
||||||
while True:
|
|
||||||
tmp = []
|
|
||||||
while re.search(TsanAnalyzer.RACE_VERIFIER_LINE, self.line_):
|
|
||||||
tmp.append(self.line_)
|
|
||||||
self.ReadLine()
|
|
||||||
while re.search(TsanAnalyzer.THREAD_CREATION_STR, self.line_):
|
|
||||||
tmp.extend(self.ReadSection())
|
|
||||||
if re.search(TsanAnalyzer.TSAN_RACE_DESCRIPTION, self.line_):
|
|
||||||
tmp.extend(self.ReadSection())
|
|
||||||
ret.append(tmp) # includes RaceVerifier and thread creation stacks
|
|
||||||
elif (re.search(TsanAnalyzer.TSAN_WARNING_DESCRIPTION, self.line_) and
|
|
||||||
not common.IsWindows()): # workaround for http://crbug.com/53198
|
|
||||||
tmp.extend(self.ReadSection())
|
|
||||||
ret.append(tmp)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
tmp = []
|
|
||||||
if re.search(TsanAnalyzer.TSAN_ASSERTION, self.line_):
|
|
||||||
tmp.extend(self.ReadTillTheEnd())
|
|
||||||
ret.append(tmp)
|
|
||||||
break
|
|
||||||
|
|
||||||
match = re.search("used_suppression:\s+([0-9]+)\s(.*)", self.line_)
|
|
||||||
if match:
|
|
||||||
count, supp_name = match.groups()
|
|
||||||
count = int(count)
|
|
||||||
self.used_suppressions[supp_name] += count
|
|
||||||
self.cur_fd_.close()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def GetReports(self, files):
|
|
||||||
'''Extracts reports from a set of files.
|
|
||||||
|
|
||||||
Reads a set of files and returns a list of all discovered
|
|
||||||
ThreadSanitizer race reports. As a side effect, populates
|
|
||||||
self.used_suppressions with appropriate info.
|
|
||||||
'''
|
|
||||||
|
|
||||||
global TheAddressTable
|
|
||||||
if self._use_gdb:
|
|
||||||
TheAddressTable = gdb_helper.AddressTable()
|
|
||||||
else:
|
|
||||||
TheAddressTable = None
|
|
||||||
reports = []
|
|
||||||
self.used_suppressions = defaultdict(int)
|
|
||||||
for file in files:
|
|
||||||
reports.extend(self.ParseReportFile(file))
|
|
||||||
if self._use_gdb:
|
|
||||||
TheAddressTable.ResolveAll()
|
|
||||||
# Make each line of each report a string.
|
|
||||||
reports = map(lambda(x): map(str, x), reports)
|
|
||||||
return [''.join(report_lines) for report_lines in reports]
|
|
||||||
|
|
||||||
def Report(self, files, testcase, check_sanity=False):
|
|
||||||
'''Reads in a set of files and prints ThreadSanitizer report.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
files: A list of filenames.
|
|
||||||
check_sanity: if true, search for SANITY_TEST_SUPPRESSIONS
|
|
||||||
'''
|
|
||||||
|
|
||||||
# We set up _cur_testcase class-wide variable to avoid passing it through
|
|
||||||
# about 5 functions.
|
|
||||||
self._cur_testcase = testcase
|
|
||||||
reports = self.GetReports(files)
|
|
||||||
self._cur_testcase = None # just in case, shouldn't be used anymore
|
|
||||||
|
|
||||||
common.PrintUsedSuppressionsList(self.used_suppressions)
|
|
||||||
|
|
||||||
|
|
||||||
retcode = 0
|
|
||||||
if reports:
|
|
||||||
sys.stdout.flush()
|
|
||||||
sys.stderr.flush()
|
|
||||||
logging.info("FAIL! Found %i report(s)" % len(reports))
|
|
||||||
for report in reports:
|
|
||||||
logging.info('\n' + report)
|
|
||||||
sys.stdout.flush()
|
|
||||||
retcode = -1
|
|
||||||
|
|
||||||
# Report tool's insanity even if there were errors.
|
|
||||||
if (check_sanity and
|
|
||||||
TsanAnalyzer.SANITY_TEST_SUPPRESSION not in self.used_suppressions):
|
|
||||||
logging.error("FAIL! Sanity check failed!")
|
|
||||||
retcode = -3
|
|
||||||
|
|
||||||
if retcode != 0:
|
|
||||||
return retcode
|
|
||||||
|
|
||||||
logging.info("PASS: No reports found")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
'''For testing only. The TsanAnalyzer class should be imported instead.'''
|
|
||||||
parser = optparse.OptionParser("usage: %prog [options] <files to analyze>")
|
|
||||||
parser.add_option("", "--source_dir",
|
|
||||||
help="path to top of source tree for this build"
|
|
||||||
"(used to normalize source paths in baseline)")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
if not args:
|
|
||||||
parser.error("no filename specified")
|
|
||||||
filenames = args
|
|
||||||
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
analyzer = TsanAnalyzer(options.source_dir, use_gdb=True)
|
|
||||||
return analyzer.Report(filenames, None)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main())
|
|
|
@ -1,16 +0,0 @@
|
||||||
# The rules in this file are only applied at compile time.
|
|
||||||
# Because the Chrome buildsystem does not automatically touch the files
|
|
||||||
# mentioned here, changing this file requires clobbering all TSan v2 bots.
|
|
||||||
#
|
|
||||||
# Please think twice before you add or remove these rules.
|
|
||||||
# Data races should typically go to suppressions.txt.
|
|
||||||
|
|
||||||
# See http://crbug.com/102327
|
|
||||||
fun:*ThreadData*Initialize*
|
|
||||||
|
|
||||||
# Known benign races on histograms. See http://crbug.com/62694.
|
|
||||||
src:base/metrics/histogram_samples.cc
|
|
||||||
src:base/metrics/sample_vector.cc
|
|
||||||
|
|
||||||
# See http://crbug.com/172104
|
|
||||||
fun:*v8*internal*ThreadEntry*
|
|
|
@ -1,121 +0,0 @@
|
||||||
# False positives in libflashplayer.so and libglib.so. Since we don't
|
|
||||||
# instrument them, we cannot reason about the synchronization in them.
|
|
||||||
race:libflashplayer.so
|
|
||||||
race:libglib*.so
|
|
||||||
|
|
||||||
# Intentional race in ToolsSanityTest.DataRace in base_unittests.
|
|
||||||
race:base/tools_sanity_unittest.cc
|
|
||||||
|
|
||||||
# Data race on WatchdogCounter [test-only]
|
|
||||||
race:base/threading/watchdog_unittest.cc
|
|
||||||
|
|
||||||
# Races in libevent, http://crbug.com/23244
|
|
||||||
race:libevent/event.c
|
|
||||||
|
|
||||||
# http://crbug.com/46840
|
|
||||||
race:history::HistoryBackend::DeleteFTSIndexDatabases
|
|
||||||
race:history::InMemoryHistoryBackend::Init
|
|
||||||
|
|
||||||
# http://crbug.com/84094
|
|
||||||
race:sqlite3StatusSet
|
|
||||||
race:pcache1EnforceMaxPage
|
|
||||||
race:pcache1AllocPage
|
|
||||||
|
|
||||||
# http://crbug.com/102327.
|
|
||||||
# Test-only race, won't fix.
|
|
||||||
race:tracked_objects::ThreadData::ShutdownSingleThreadedCleanup
|
|
||||||
|
|
||||||
# http://crbug.com/115540
|
|
||||||
race:*GetCurrentThreadIdentifier
|
|
||||||
|
|
||||||
# http://crbug.com/120808
|
|
||||||
race:base/threading/watchdog.cc
|
|
||||||
|
|
||||||
# http://crbug.com/157586
|
|
||||||
race:third_party/libvpx/source/libvpx/vp8/decoder/threading.c
|
|
||||||
|
|
||||||
# http://crbug.com/158718
|
|
||||||
race:third_party/ffmpeg/libavcodec/pthread.c
|
|
||||||
race:third_party/ffmpeg/libavcodec/vp8.c
|
|
||||||
race:third_party/ffmpeg/libavutil/mem.c
|
|
||||||
race:*HashFrameForTesting
|
|
||||||
race:third_party/ffmpeg/libavcodec/h264pred.c
|
|
||||||
race:media::ReleaseData
|
|
||||||
|
|
||||||
# http://crbug.com/158922
|
|
||||||
race:third_party/libvpx/source/libvpx/vp8/encoder/*
|
|
||||||
|
|
||||||
# See http://crbug.com/181502
|
|
||||||
race:_M_rep
|
|
||||||
race:_M_is_leaked
|
|
||||||
|
|
||||||
# http://crbug.com/189177
|
|
||||||
race:thread_manager
|
|
||||||
race:v8::Locker::Initialize
|
|
||||||
|
|
||||||
# http://crbug.com/223352
|
|
||||||
race:uprv_malloc_46
|
|
||||||
race:uprv_realloc_46
|
|
||||||
|
|
||||||
# http://crbug.com/223955
|
|
||||||
race:PassRefPtr
|
|
||||||
|
|
||||||
# http://crbug.com/224617
|
|
||||||
race:base::debug::TraceEventTestFixture_TraceSampling_Test::TestBody
|
|
||||||
|
|
||||||
# http://crbug.com/244368
|
|
||||||
race:skia::BeginPlatformPaint
|
|
||||||
|
|
||||||
# http://crbug.com/244385
|
|
||||||
race:unixTempFileDir
|
|
||||||
|
|
||||||
# http://crbug.com/244774
|
|
||||||
race:webrtc::RTPReceiver::ProcessBitrate
|
|
||||||
race:webrtc::RTPSender::ProcessBitrate
|
|
||||||
race:webrtc::VideoCodingModuleImpl::Decode
|
|
||||||
race:webrtc::RTPSender::SendOutgoingData
|
|
||||||
race:webrtc::VP8EncoderImpl::GetEncodedPartitions
|
|
||||||
race:webrtc::VP8EncoderImpl::Encode
|
|
||||||
race:webrtc::ViEEncoder::DeliverFrame
|
|
||||||
|
|
||||||
# http://crbug.com/246968
|
|
||||||
race:webrtc::VideoCodingModuleImpl::RegisterPacketRequestCallback
|
|
||||||
|
|
||||||
# http://crbug.com/246970
|
|
||||||
race:webrtc::EventPosix::StartTimer
|
|
||||||
|
|
||||||
# http://crbug.com/246974
|
|
||||||
race:content::GpuWatchdogThread::CheckArmed
|
|
||||||
|
|
||||||
# http://crbug.com/248101
|
|
||||||
race:sqlite3Config
|
|
||||||
race:mem0
|
|
||||||
|
|
||||||
# http://crbug.com/257396
|
|
||||||
race:base::debug::TraceEventTestFixture_TraceSamplingScope_Test::TestBody
|
|
||||||
|
|
||||||
# http://crbug.com/257543
|
|
||||||
race:*GetObjectFromEntryAddress
|
|
||||||
|
|
||||||
# http://crbug.com/268924
|
|
||||||
race:base::g_power_monitor
|
|
||||||
race:base::PowerMonitor::PowerMonitor
|
|
||||||
race:base::PowerMonitor::AddObserver
|
|
||||||
|
|
||||||
# http://crbug.com/268941
|
|
||||||
race:tracked_objects::ThreadData::tls_index_
|
|
||||||
|
|
||||||
# http://crbug.com/268946
|
|
||||||
race:CommandLine::HasSwitch
|
|
||||||
|
|
||||||
# http://crbug.com/269965
|
|
||||||
race:DesktopMediaPickerModelTest_UpdateThumbnail_Test
|
|
||||||
|
|
||||||
# http://crbug.com/270037
|
|
||||||
race:gLibCleanupFunctions
|
|
||||||
|
|
||||||
# http://crbug.com/270675
|
|
||||||
race:net::RuleBasedHostResolverProc::Resolve
|
|
||||||
|
|
||||||
# http://crbug.com/272095
|
|
||||||
race:base::g_top_manager
|
|
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import urllib2
|
|
||||||
|
|
||||||
import suppressions
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
supp = suppressions.GetSuppressions()
|
|
||||||
|
|
||||||
all_supps = []
|
|
||||||
for supps in supp.values():
|
|
||||||
all_supps += [s.description for s in supps]
|
|
||||||
sys.stdout.write(urllib2.urlopen(
|
|
||||||
'http://chromium-build-logs.appspot.com/unused_suppressions',
|
|
||||||
'\n'.join(all_supps)).read())
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
|
@ -1,124 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# This is a small script for manually launching valgrind, along with passing
|
|
||||||
# it the suppression file, and some helpful arguments (automatically attaching
|
|
||||||
# the debugger on failures, etc). Run it from your repo root, something like:
|
|
||||||
# $ sh ./tools/valgrind/valgrind.sh ./out/Debug/chrome
|
|
||||||
#
|
|
||||||
# This is mostly intended for running the chrome browser interactively.
|
|
||||||
# To run unit tests, you probably want to run chrome_tests.sh instead.
|
|
||||||
# That's the script used by the valgrind buildbot.
|
|
||||||
|
|
||||||
export THISDIR=`dirname $0`
|
|
||||||
|
|
||||||
setup_memcheck() {
|
|
||||||
RUN_COMMAND="valgrind"
|
|
||||||
GDB=gdb
|
|
||||||
EXE_INFO=$(file $1)
|
|
||||||
if [[ $? -eq 0 ]]; then
|
|
||||||
# Prefer a gdb that matches the executable if it's available.
|
|
||||||
if [[ "$EXE_INFO" == *32-bit* && -x /usr/bin/gdb32 ]]; then
|
|
||||||
GDB="/usr/bin/gdb32";
|
|
||||||
elif [[ "$EXE_INFO" == *64-bit* && -x /usr/bin/gdb64 ]]; then
|
|
||||||
GDB="/usr/bin/gdb64";
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Prompt to attach gdb when there was an error detected.
|
|
||||||
DEFAULT_TOOL_FLAGS=("--db-command=$GDB -nw %f %p" "--db-attach=yes" \
|
|
||||||
# Keep the registers in gdb in sync with the code.
|
|
||||||
"--vex-iropt-register-updates=allregs-at-mem-access" \
|
|
||||||
# Overwrite newly allocated or freed objects
|
|
||||||
# with 0x41 to catch inproper use.
|
|
||||||
"--malloc-fill=41" "--free-fill=41" \
|
|
||||||
# Increase the size of stacks being tracked.
|
|
||||||
"--num-callers=30")
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_tsan() {
|
|
||||||
RUN_COMMAND="valgrind-tsan.sh"
|
|
||||||
IGNORE_FILE="$THISDIR/tsan/ignores.txt"
|
|
||||||
DEFAULT_TOOL_FLAGS=("--announce-threads" "--pure-happens-before=yes" \
|
|
||||||
"--ignore=$IGNORE_FILE")
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_unknown() {
|
|
||||||
echo "Unknown tool \"$TOOL_NAME\" specified, the result is not guaranteed"
|
|
||||||
DEFAULT_TOOL_FLAGS=()
|
|
||||||
}
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ $# -eq 0 ]; then
|
|
||||||
echo "usage: <command to run> <arguments ...>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
TOOL_NAME="memcheck"
|
|
||||||
declare -a DEFAULT_TOOL_FLAGS[0]
|
|
||||||
|
|
||||||
# Select a tool different from memcheck with --tool=TOOL as a first argument
|
|
||||||
TMP_STR=`echo $1 | sed 's/^\-\-tool=//'`
|
|
||||||
if [ "$TMP_STR" != "$1" ]; then
|
|
||||||
TOOL_NAME="$TMP_STR"
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
if echo "$@" | grep "\-\-tool" ; then
|
|
||||||
echo "--tool=TOOL must be the first argument" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
case $TOOL_NAME in
|
|
||||||
memcheck*) setup_memcheck "$1";;
|
|
||||||
tsan*) setup_tsan;;
|
|
||||||
*) setup_unknown;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
|
|
||||||
SUPPRESSIONS="$THISDIR/$TOOL_NAME/suppressions.txt"
|
|
||||||
|
|
||||||
CHROME_VALGRIND=`sh $THISDIR/locate_valgrind.sh`
|
|
||||||
if [ "$CHROME_VALGRIND" = "" ]
|
|
||||||
then
|
|
||||||
# locate_valgrind.sh failed
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Using valgrind binaries from ${CHROME_VALGRIND}"
|
|
||||||
|
|
||||||
set -x
|
|
||||||
PATH="${CHROME_VALGRIND}/bin:$PATH"
|
|
||||||
# We need to set these variables to override default lib paths hard-coded into
|
|
||||||
# Valgrind binary.
|
|
||||||
export VALGRIND_LIB="$CHROME_VALGRIND/lib/valgrind"
|
|
||||||
export VALGRIND_LIB_INNER="$CHROME_VALGRIND/lib/valgrind"
|
|
||||||
|
|
||||||
# G_SLICE=always-malloc: make glib use system malloc
|
|
||||||
# NSS_DISABLE_UNLOAD=1: make nss skip dlclosing dynamically loaded modules,
|
|
||||||
# which would result in "obj:*" in backtraces.
|
|
||||||
# NSS_DISABLE_ARENA_FREE_LIST=1: make nss use system malloc
|
|
||||||
# G_DEBUG=fatal_warnings: make GTK abort on any critical or warning assertions.
|
|
||||||
# If it crashes on you in the Options menu, you hit bug 19751,
|
|
||||||
# comment out the G_DEBUG=fatal_warnings line.
|
|
||||||
#
|
|
||||||
# GTEST_DEATH_TEST_USE_FORK=1: make gtest death tests valgrind-friendly
|
|
||||||
#
|
|
||||||
# When everyone has the latest valgrind, we might want to add
|
|
||||||
# --show-possibly-lost=no
|
|
||||||
# to ignore possible but not definite leaks.
|
|
||||||
|
|
||||||
G_SLICE=always-malloc \
|
|
||||||
NSS_DISABLE_UNLOAD=1 \
|
|
||||||
NSS_DISABLE_ARENA_FREE_LIST=1 \
|
|
||||||
G_DEBUG=fatal_warnings \
|
|
||||||
GTEST_DEATH_TEST_USE_FORK=1 \
|
|
||||||
$RUN_COMMAND \
|
|
||||||
--trace-children=yes \
|
|
||||||
--leak-check=yes \
|
|
||||||
--suppressions="$SUPPRESSIONS" \
|
|
||||||
"${DEFAULT_TOOL_FLAGS[@]}" \
|
|
||||||
"$@"
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,222 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Copyright (c) 2011 The Chromium Authors. All rights reserved.
|
|
||||||
# Use of this source code is governed by a BSD-style license that can be
|
|
||||||
# found in the LICENSE file.
|
|
||||||
|
|
||||||
# This script can be used by waterfall sheriffs to fetch the status
|
|
||||||
# of Valgrind bots on the memory waterfall and test if their local
|
|
||||||
# suppressions match the reports on the waterfall.
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
THISDIR=$(dirname "${0}")
|
|
||||||
LOGS_DIR=$THISDIR/waterfall.tmp
|
|
||||||
WATERFALL_PAGE="http://build.chromium.org/p/chromium.memory/builders"
|
|
||||||
WATERFALL_FYI_PAGE="http://build.chromium.org/p/chromium.memory.fyi/builders"
|
|
||||||
|
|
||||||
download() {
|
|
||||||
# Download a file.
|
|
||||||
# $1 = URL to download
|
|
||||||
# $2 = Path to the output file
|
|
||||||
# {{{1
|
|
||||||
if [ "$(which curl)" != "" ]
|
|
||||||
then
|
|
||||||
if ! curl -s -o "$2" "$1"
|
|
||||||
then
|
|
||||||
echo
|
|
||||||
echo "Failed to download '$1'... aborting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
elif [ "$(which wget)" != "" ]
|
|
||||||
then
|
|
||||||
if ! wget "$1" -O "$2" -q
|
|
||||||
then
|
|
||||||
echo
|
|
||||||
echo "Failed to download '$1'... aborting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Need either curl or wget to download stuff... aborting"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# }}}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch_logs() {
|
|
||||||
# Fetch Valgrind logs from the waterfall {{{1
|
|
||||||
|
|
||||||
# TODO(timurrrr,maruel): use JSON, see
|
|
||||||
# http://build.chromium.org/p/chromium.memory/json/help
|
|
||||||
|
|
||||||
rm -rf "$LOGS_DIR" # Delete old logs
|
|
||||||
mkdir "$LOGS_DIR"
|
|
||||||
|
|
||||||
echo "Fetching the list of builders..."
|
|
||||||
download $1 "$LOGS_DIR/builders"
|
|
||||||
SLAVES=$(grep "<a href=\"builders\/" "$LOGS_DIR/builders" | \
|
|
||||||
grep 'td class="box"' | \
|
|
||||||
sed "s/.*<a href=\"builders\///" | sed "s/\".*//" | \
|
|
||||||
sort | uniq)
|
|
||||||
|
|
||||||
for S in $SLAVES
|
|
||||||
do
|
|
||||||
SLAVE_URL=$1/$S
|
|
||||||
SLAVE_NAME=$(echo $S | sed -e "s/%20/ /g" -e "s/%28/(/g" -e "s/%29/)/g")
|
|
||||||
echo -n "Fetching builds by slave '${SLAVE_NAME}'"
|
|
||||||
download $SLAVE_URL?numbuilds=${NUMBUILDS} "$LOGS_DIR/slave_${S}"
|
|
||||||
|
|
||||||
# We speed up the 'fetch' step by skipping the builds/tests which succeeded.
|
|
||||||
# TODO(timurrrr): OTOH, we won't be able to check
|
|
||||||
# if some suppression is not used anymore.
|
|
||||||
#
|
|
||||||
# The awk script here joins the lines ending with </td> to make it possible
|
|
||||||
# to find the failed builds.
|
|
||||||
LIST_OF_BUILDS=$(cat "$LOGS_DIR/slave_$S" | \
|
|
||||||
awk 'BEGIN { buf = "" }
|
|
||||||
{
|
|
||||||
if ($0 ~ /<\/td>/) { buf = (buf $0); }
|
|
||||||
else {
|
|
||||||
if (buf) { print buf; buf="" }
|
|
||||||
print $0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
END {if (buf) print buf}' | \
|
|
||||||
grep "success\|failure" | \
|
|
||||||
head -n $NUMBUILDS | \
|
|
||||||
grep "failure" | \
|
|
||||||
grep -v "failed compile" | \
|
|
||||||
sed "s/.*\/builds\///" | sed "s/\".*//")
|
|
||||||
|
|
||||||
for BUILD in $LIST_OF_BUILDS
|
|
||||||
do
|
|
||||||
# We'll fetch a few tiny URLs now, let's use a temp file.
|
|
||||||
TMPFILE=$(mktemp -t memory_waterfall.XXXXXX)
|
|
||||||
download $SLAVE_URL/builds/$BUILD "$TMPFILE"
|
|
||||||
|
|
||||||
REPORT_FILE="$LOGS_DIR/report_${S}_${BUILD}"
|
|
||||||
rm -f $REPORT_FILE 2>/dev/null || true # make sure it doesn't exist
|
|
||||||
|
|
||||||
REPORT_URLS=$(grep -o "[0-9]\+/steps/\(memory\|heapcheck\).*/logs/[0-9A-F]\{16\}" \
|
|
||||||
"$TMPFILE" \
|
|
||||||
|| true) # `true` is to succeed on empty output
|
|
||||||
FAILED_TESTS=$(grep -o "[0-9]\+/steps/\(memory\|heapcheck\).*/logs/[A-Za-z0-9_.]\+" \
|
|
||||||
"$TMPFILE" | grep -v "[0-9A-F]\{16\}" \
|
|
||||||
| grep -v "stdio" || true)
|
|
||||||
|
|
||||||
for REPORT in $REPORT_URLS
|
|
||||||
do
|
|
||||||
download "$SLAVE_URL/builds/$REPORT/text" "$TMPFILE"
|
|
||||||
echo "" >> "$TMPFILE" # Add a newline at the end
|
|
||||||
cat "$TMPFILE" | tr -d '\r' >> "$REPORT_FILE"
|
|
||||||
done
|
|
||||||
|
|
||||||
for FAILURE in $FAILED_TESTS
|
|
||||||
do
|
|
||||||
echo -n "FAILED:" >> "$REPORT_FILE"
|
|
||||||
echo "$FAILURE" | sed -e "s/.*\/logs\///" -e "s/\/.*//" \
|
|
||||||
>> "$REPORT_FILE"
|
|
||||||
done
|
|
||||||
|
|
||||||
rm "$TMPFILE"
|
|
||||||
echo $SLAVE_URL/builds/$BUILD >> "$REPORT_FILE"
|
|
||||||
done
|
|
||||||
echo " DONE"
|
|
||||||
done
|
|
||||||
# }}}
|
|
||||||
}
|
|
||||||
|
|
||||||
match_suppressions() {
|
|
||||||
PYTHONPATH=$THISDIR/../python/google \
|
|
||||||
python "$THISDIR/test_suppressions.py" $@ "$LOGS_DIR/report_"*
|
|
||||||
}
|
|
||||||
|
|
||||||
match_gtest_excludes() {
|
|
||||||
for PLATFORM in "Linux" "Chromium%20Mac" "Chromium%20OS"
|
|
||||||
do
|
|
||||||
echo
|
|
||||||
echo "Test failures on ${PLATFORM}:" | sed "s/%20/ /"
|
|
||||||
grep -h -o "^FAILED:.*" -R "$LOGS_DIR"/*${PLATFORM}* | \
|
|
||||||
grep -v "FAILS\|FLAKY" | sort | uniq | \
|
|
||||||
sed -e "s/^FAILED://" -e "s/^/ /"
|
|
||||||
# Don't put any operators between "grep | sed" and "RESULT=$PIPESTATUS"
|
|
||||||
RESULT=$PIPESTATUS
|
|
||||||
|
|
||||||
if [ "$RESULT" == 1 ]
|
|
||||||
then
|
|
||||||
echo " None!"
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo " Note: we don't check for failures already excluded locally yet"
|
|
||||||
echo " TODO(timurrrr): don't list tests we've already excluded locally"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
echo "Note: we don't print FAILS/FLAKY tests and 1200s-timeout failures"
|
|
||||||
}
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<EOF
|
|
||||||
usage: $0 fetch|match options
|
|
||||||
|
|
||||||
This script can be used by waterfall sheriffs to fetch the status
|
|
||||||
of Valgrind bots on the memory waterfall and test if their local
|
|
||||||
suppressions match the reports on the waterfall.
|
|
||||||
|
|
||||||
OPTIONS:
|
|
||||||
-h Show this message
|
|
||||||
-n N Fetch N builds from each slave.
|
|
||||||
|
|
||||||
COMMANDS:
|
|
||||||
fetch Fetch Valgrind logs from the memory waterfall
|
|
||||||
match Test the local suppression files against the downloaded logs
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
NUMBUILDS=3
|
|
||||||
|
|
||||||
CMD=$1
|
|
||||||
if [ $# != 0 ]; then
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Arguments for "match" are handled in match_suppressions
|
|
||||||
if [ "$CMD" != "match" ]; then
|
|
||||||
while getopts “hn:” OPTION
|
|
||||||
do
|
|
||||||
case $OPTION in
|
|
||||||
h)
|
|
||||||
usage
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
n)
|
|
||||||
NUMBUILDS=$OPTARG
|
|
||||||
;;
|
|
||||||
?)
|
|
||||||
usage
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND-1))
|
|
||||||
if [ $# != 0 ]; then
|
|
||||||
usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$CMD" = "fetch" ]; then
|
|
||||||
echo "Fetching $NUMBUILDS builds"
|
|
||||||
fetch_logs $WATERFALL_PAGE
|
|
||||||
fetch_logs $WATERFALL_FYI_PAGE
|
|
||||||
elif [ "$CMD" = "match" ]; then
|
|
||||||
match_suppressions $@
|
|
||||||
match_gtest_excludes
|
|
||||||
elif [ "$CMD" = "blame" ]; then
|
|
||||||
echo The blame command died of bitrot. If you need it, please reimplement it.
|
|
||||||
echo Reimplementation is blocked on http://crbug.com/82688
|
|
||||||
else
|
|
||||||
usage
|
|
||||||
exit 1
|
|
||||||
fi
|
|
Loading…
Reference in New Issue