blob: 132fc27788eda0c5fa6b26913d1f55209497d191 [file] [edit]
#!/usr/bin/perl
# **********************************************************
# Copyright (c) 2016-2025 Google, Inc. All rights reserved.
# **********************************************************
# Dr. Memory: the memory debugger
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation;
# version 2.1 of the License, and no later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
# XXX: we should share this w/ DR.
# Build-and-test driver for Travis CI.
# Travis uses the exit code to check success, so we need a layer outside of
# ctest on runsuite.
# We stick with runsuite rather than creating a parallel scheme using
# a Travis matrix of builds.
# Travis only supports Linux and Mac, so we're ok relying on perl.
use strict;
use Cwd 'abs_path';
use File::Basename;
my $mydir = dirname(abs_path($0));
my $is_CI = 0;
# Forward args to runsuite.cmake:
my $args = '';
for (my $i = 0; $i <= $#ARGV; $i++) {
$is_CI = 1 if ($ARGV[$i] eq 'travis');
if ($i == 0) {
$args .= ",$ARGV[$i]";
} else {
# We don't use a backslash to escape ; b/c we'll quote below, and
# the backslash is problematically converted to / by Cygwin perl.
$args .= ";$ARGV[$i]";
}
}
my $osdir = $mydir;
if ($^O eq 'cygwin') {
# CMake is native Windows so pass it a Windows path.
# We use the full path to cygpath as git's cygpath is earlier on
# the PATH for AppVeyor and it fails.
$osdir = `/usr/bin/cygpath -wi \"$mydir\"`;
chomp $osdir;
}
# We have no way to access the log files, so we use -VV to ensure
# we can diagnose failures.
# We tee to stdout to provide incremental output and avoid the 10-min
# no-output timeout on Travis.
# If we're on UNIX or we have a Cygwin perl, we do this via a fork.
my $res = '';
my $child = 0;
my $outfile = '';
if ($^O ne 'MSWin32') {
print "Forking child for stdout tee\n";
$child = open(CHILD, '-|');
die "Failed to fork: $!" if (!defined($child));
} else {
$outfile = "runsuite_output.txt";
}
if ($child) {
# Parent
# i#4126: We include extra printing to help diagnose hangs on Travis.
if ($^O ne 'cygwin') {
print "Parent tee-ing child stdout...\n";
local $SIG{ALRM} = sub {
print "\nxxxxxxxxxx 30s elapsed xxxxxxxxxxx\n";
alarm(30);
};
alarm(30);
while (<CHILD>) {
print STDOUT $_;
$res .= $_;
}
} else {
while (<CHILD>) {
print STDOUT $_;
$res .= $_;
}
}
close(CHILD);
} elsif ($ENV{'CI_TARGET'} eq 'package') {
# A package build.
my $build = "0";
if ($ENV{'VERSION_NUMBER'} =~ /-(\d+)$/) {
$build = $1;
}
if ($args eq '') {
$args = ",";
} else {
$args .= ";";
}
$args .= "drmem_only;build=${build}";
if ($ENV{'DEPLOY_DOCS'} eq 'yes') {
$args .= ";copy_docs";
}
if ($^O eq 'darwin' || $^O eq 'MacOS') {
$args .= ";64_only";
}
if ($ENV{'VERSION_NUMBER'} =~ /^(\d+\.\d+\.\d+)/) {
my $version = $1;
$args .= ";version=${version}";
}
my $cmd = "ctest -VV -S \"${osdir}/../package.cmake${args}\"";
print "Running ${cmd}\n";
if ($^O eq 'MSWin32') {
system("${cmd} 2>&1 | tee ${outfile}");
} else {
system("${cmd} 2>&1");
exit 0;
}
} else {
# Despite creating larger log files, -VV makes it easier to diagnose issues.
my $cmd = "ctest --output-on-failure -VV -S \"${osdir}/runsuite.cmake${args}\"";
print "Running ${cmd}\n";
if ($^O eq 'MSWin32') {
system("${cmd} 2>&1 | tee ${outfile}");
print "Finished running ${cmd}\n";
} else {
system("${cmd} 2>&1");
print "Finished running ${cmd}\n";
exit 0;
}
}
if ($^O eq 'MSWin32') {
open my $handle, '<', "$outfile" or die "Failed to open teed ${outfile}: $!";
$res = do {
local $/; <$handle>
};
}
my @lines = split('\n', $res);
my $should_print = 0;
my $exit_code = 0;
for (my $i = 0; $i <= $#lines; ++$i) {
my $line = $lines[$i];
my $fail = 0;
my $name = '';
$should_print = 1 if ($line =~ /^RESULTS/);
if ($line =~ /^([-\w]+):.*\*\*/) {
$name = $1;
if ($line =~ /build errors/ ||
$line =~ /configure errors/ ||
$line =~ /tests failed:/) {
$fail = 1;
} elsif ($line =~ /(\d+) tests failed, of which (\d+)/) {
$fail = 1 if ($2 < $1);
}
} elsif ($line =~ /^\s*ERROR: diff contains/) {
$fail = 1;
$should_print = 1;
$name = "diff pre-commit checks";
} elsif ($line =~ /^FAILED: CMakeFiles\/package/) {
$fail = 1;
$should_print = 1;
$name = "packaging step";
}
if ($fail && $is_CI && $line =~ /tests failed/) {
my $is_32 = $line =~ /-32/;
my %ignore_failures_32 = ();
my %ignore_failures_64 = ();
if ($^O eq 'cygwin' ||
$^O eq 'MSWin32') {
# FIXME i#1938: ignoring certain Windows CI test failures until
# we get all tests passing.
%ignore_failures_32 = (
'procterm' => 1,
'winthreads' => 1,
'malloc_callstacks' => 1,
'app_suite.pattern' => 1,
'app_suite' => 1,
# TODO i#2180/i#2334: evaluate why failing on GA CI.
'cs2bug' => 1,
'reachable' => 1,
'wincrt' => 1,
'cs2bugMTdZI' => 1,
'cs2bugMTd' => 1,
'cs2bugMD' => 1,
'cs2bugMDd' => 1,
'gdi' => 1,
'handle' => 1,
'handle_only' => 1,
'pcache-use' => 1,
'drmf_drsyscall_test' => 1,
'drmf_strace_test' => 1,
'drstrace_unit_tests' => 1,
'syscalls_win' => 1,
'fuzz_threads' => 1,
# TODO i#2342: These are hitting a DR encoding assert. Maybe we
# should just drop wrap_ support anyway. Also xref i#1741.
'wrap_malloc' => 1,
'wrap_cs2bug' => 1,
'wrap_operators' => 1,
'wrap_wincrt' => 1,
'wrap_wincrtdbg' => 1,
'wrap_cs2bugMTd' => 1,
'wrap_operatorsMDd' => 1,
'leak_string' => 1,
# TODO i#2375: Fix DR to avoid test failures.
'umbra_client_faulty_redzone' => 1,
);
# FIXME i#2180: ignoring certain AppVeyor x64-full-mode failures until
# we get all tests passing.
%ignore_failures_64 = (
'procterm' => 1,
'badjmp' => 1,
'cs2bug' => 1,
'winthreads' => 1,
'procterm.nativeparent' => 1,
'malloc_callstacks' => 1,
'reachable' => 1,
'suppress' => 1, # i#2338
'suppress-genoffs' => 1,
'suppress-gensyms' => 1,
'wincrt' => 1,
'cs2bugMTd' => 1,
'cs2bugMTdZI' => 1,
'cs2bugMD' => 1,
'cs2bugMDd' => 1,
'operatorsMDd' => 1,
'gdi' => 1,
'syscalls_win' => 1,
'handle_only' => 1,
'nudge' => 1,
'syscall_file_all' => 1,
'syscall_file_gen' => 1,
'handle' => 1,
'drstrace_unit_tests' => 1,
'app_suite.pattern' => 1,
# TODO i#2180/i#2334: These have an extra invalid heap arg but it's
# not printed out by the auto-print-results.txt: we need to get that
# and suppress or fix.
'fuzz_buffer.cpp' => 1,
'fuzz_buffer.cpp.demangled' => 1,
# TODO i#2180/i#2334: extra uninit but not printed out on CI!
'nosyms' => 1,
# TODO i#2180/i#2334: extra potential error but not printed out on CI!
'allowlist_app' => 1,
'allowlist_justlib' => 1,
'allowlist_src' => 1,
'allowlist_srclib' => 1,
);
} elsif ($^O eq 'darwin' || $^O eq 'MacOS') {
%ignore_failures_32 = ('malloc' => 1); # i#2038
%ignore_failures_64 = ('malloc' => 1);
} else {
%ignore_failures_32 = ('pcache-use' => 1, # i#2202
'fuzz_threads' => 1, # i#2242
# TODO i#2491: AMD 32-bit assert.
'selfmod' => 1,
'clone' => 1,
'syscalls_unix' => 1,
'pthread_test' => 1,
'realloc' => 1,
'drmf_drsyscall_test' => 1,
'drmf_strace_test' => 1,
# XXX: We should probably drop wrap_ support as we
# do not have the resources to maintain it.
'wrap_cs2bug' => 1,
'wrap_operators' => 1,
'app_suite.pattern' => 1,
'app_suite' => 1);
%ignore_failures_64 = ('pcache' => 1, # i#2243
'app_suite.pattern' => 1);
}
# Read ahead to examine the test failures:
$fail = 0;
my $num_ignore = 0;
for (my $j = $i+1; $j <= $#lines; ++$j) {
my $test;
if ($lines[$j] =~ /^\t(\S+)\s/) {
$test = $1;
if (($is_32 && $ignore_failures_32{$test}) ||
(!$is_32 && $ignore_failures_64{$test})) {
$lines[$j] = "\t(ignore: i#1938) " . $lines[$j];
$num_ignore++;
} elsif ($test =~ /_FLAKY$/) {
# Don't count toward failure.
} else {
$fail = 1;
}
} else {
last if ($lines[$j] =~ /^\S/);
}
}
$line =~ s/: \*/, but ignoring $num_ignore for i1938: */;
}
if ($fail) {
$exit_code++;
print "\n====> FAILURE in $name <====\n";
}
print "$line\n" if ($should_print);
if ($line =~ /Please check '([^']+)' for errors/) {
my $log = $1;
if ($^O eq 'cygwin') {
$log = `/usr/bin/cygpath -u \"$log\"`;
}
chomp $log;
if (open(LOG, "< $log")) {
print "\n\n----------------- START $log -----------\n";
while (<LOG>) {
print $_;
}
print "\n----------------- END $log -----------\n\n";
} else {
print "Failed to open $log\n";
}
close(LOG);
}
}
if (!$should_print) {
print "Error: RESULTS line not found\n";
$exit_code++;
}
exit $exit_code;