Skip to content

Commit

Permalink
Add a run_tests flag that captures core dumps from engine unit tests …
Browse files Browse the repository at this point in the history
…and runs a GDB script (flutter#27742)
  • Loading branch information
jason-simmons authored Jul 30, 2021
1 parent 169bb4c commit 9d840d1
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
22 changes: 22 additions & 0 deletions testing/analyze_core_dump.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/bash

# Gather information from a core dump.
#
# This script can be invoked by the run_tests.py script after an
# engine test crashes.

BUILDROOT=$1
EXE=$2
CORE=$3
OUTPUT=$4

UNAME=$(uname)
if [ "$UNAME" == "Linux" ]; then
if [ -x "$(command -v gdb)" ]; then
GDB=gdb
else
GDB=$BUILDROOT/third_party/android_tools/ndk/prebuilt/linux-x86_64/bin/gdb
fi
echo "GDB=$GDB"
$GDB $EXE $CORE --batch -ex "thread apply all bt" > $OUTPUT
fi
34 changes: 29 additions & 5 deletions testing/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import glob
import os
import re
import shutil
import subprocess
import sys
import time
Expand Down Expand Up @@ -134,12 +135,33 @@ def RunEngineExecutable(build_dir, executable_name, filter, flags=[],
else:
test_command = [ executable ] + flags

RunCmd(test_command, cwd=cwd, forbidden_output=forbidden_output, expect_failure=expect_failure, env=env)


def RunCCTests(build_dir, filter, coverage):
try:
RunCmd(test_command, cwd=cwd, forbidden_output=forbidden_output, expect_failure=expect_failure, env=env)
except:
# The LUCI environment may provide a variable containing a directory path
# for additional output files that will be uploaded to cloud storage.
# If the command generated a core dump, then run a script to analyze
# the dump and output a report that will be uploaded.
luci_test_outputs_path = os.environ.get('FLUTTER_TEST_OUTPUTS_DIR')
core_path = os.path.join(cwd, 'core')
if luci_test_outputs_path and os.path.exists(core_path) and os.path.exists(unstripped_exe):
dump_path = os.path.join(luci_test_outputs_path, '%s_%s.txt' % (executable_name, sys.platform))
print 'Writing core dump analysis to %s' % dump_path
subprocess.call([
os.path.join(buildroot_dir, 'flutter', 'testing', 'analyze_core_dump.sh'),
buildroot_dir, unstripped_exe, core_path, dump_path,
])
os.unlink(core_path)
raise


def RunCCTests(build_dir, filter, coverage, capture_core_dump):
print("Running Engine Unit-tests.")

if capture_core_dump and IsLinux():
import resource
resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY))

# Not all of the engine unit tests are designed to be run more than once.
non_repeatable_shuffle_flags = [
"--gtest_shuffle",
Expand Down Expand Up @@ -523,6 +545,8 @@ def main():
help='Filter parameter for which objc tests to run (example: "IosUnitTestsTests/SemanticsObjectTest/testShouldTriggerAnnouncement")')
parser.add_argument('--coverage', action='store_true', default=None,
help='Generate coverage reports for each unit test framework run.')
parser.add_argument('--engine-capture-core-dump', dest='engine_capture_core_dump', action='store_true',
default=False, help='Capture core dumps from crashes of engine tests.')

args = parser.parse_args()

Expand All @@ -537,7 +561,7 @@ def main():

engine_filter = args.engine_filter.split(',') if args.engine_filter else None
if 'engine' in types:
RunCCTests(build_dir, engine_filter, args.coverage)
RunCCTests(build_dir, engine_filter, args.coverage, args.engine_capture_core_dump)

if 'dart' in types:
assert not IsWindows(), "Dart tests can't be run on windows. https://github.com/flutter/flutter/issues/36301."
Expand Down

0 comments on commit 9d840d1

Please sign in to comment.