Merge pull request #12730 from briansull/remove-ibc-error-string
[platform/upstream/coreclr.git] / tests / runtest.sh
1 #!/usr/bin/env bash
2
3 function print_usage {
4     echo ''
5     echo 'CoreCLR test runner script.'
6     echo ''
7     echo 'Typical command line:'
8     echo ''
9     echo 'coreclr/tests/runtest.sh'
10     echo '    --testRootDir="temp/Windows_NT.x64.Debug"'
11     echo '    --testNativeBinDir="coreclr/bin/obj/Linux.x64.Debug/tests"'
12     echo '    --coreClrBinDir="coreclr/bin/Product/Linux.x64.Debug"'
13     echo '    --mscorlibDir="windows/coreclr/bin/Product/Linux.x64.Debug"'
14     echo '    --coreFxBinDir="corefx/bin/runtime/netcoreapp-Linux-Debug-x64'
15     echo ''
16     echo 'Required arguments:'
17     echo '  --testRootDir=<path>             : Root directory of the test build (e.g. coreclr/bin/tests/Windows_NT.x64.Debug).'
18     echo '  --testNativeBinDir=<path>        : Directory of the native CoreCLR test build (e.g. coreclr/bin/obj/Linux.x64.Debug/tests).'
19     echo '  (Also required: Either --coreOverlayDir, or all of the switches --coreOverlayDir overrides)'
20     echo ''
21     echo 'Optional arguments:'
22     echo '  --coreOverlayDir=<path>          : Directory containing core binaries and test dependencies. If not specified, the'
23     echo '                                     default is testRootDir/Tests/coreoverlay. This switch overrides --coreClrBinDir,'
24     echo '                                     --mscorlibDir, and --coreFxBinDir.'
25     echo '  --coreClrBinDir=<path>           : Directory of the CoreCLR build (e.g. coreclr/bin/Product/Linux.x64.Debug).'
26     echo '  --mscorlibDir=<path>             : Directory containing the built mscorlib.dll. If not specified, it is expected to be'
27     echo '                                       in the directory specified by --coreClrBinDir.'
28     echo '  --coreFxBinDir="<path>"          : Directory with CoreFX build outputs'
29     echo '                                     (e.g. "corefx/bin/runtime/netcoreapp-Linux-Debug-x64")'
30     echo '                                     If files with the same name are present in multiple directories, the first one wins.'
31     echo '  --testDir=<path>                 : Run tests only in the specified directory. The path is relative to the directory'
32     echo '                                     specified by --testRootDir. Multiple of this switch may be specified.'
33     echo '  --testDirFile=<path>             : Run tests only in the directories specified by the file at <path>. Paths are listed'
34     echo '                                     one line, relative to the directory specified by --testRootDir.'
35     echo '  --build-overlay-only             : Build coreoverlay only, and skip running tests.'
36     echo '  --runFailingTestsOnly            : Run only the tests that are disabled on this platform due to unexpected failures.'
37     echo '                                     Failing tests are listed in coreclr/tests/failingTestsOutsideWindows.txt, one per'
38     echo '                                     line, as paths to .sh files relative to the directory specified by --testRootDir.'
39     echo '  --disableEventLogging            : Disable the events logged by both VM and Managed Code'
40     echo '  --sequential                     : Run tests sequentially (default is to run in parallel).'
41     echo '  --playlist=<path>                : Run only the tests that are specified in the file at <path>, in the same format as'
42     echo '                                     runFailingTestsOnly'
43     echo '  -v, --verbose                    : Show output from each test.'
44     echo '  -h|--help                        : Show usage information.'
45     echo '  --useServerGC                    : Enable server GC for this test run'
46     echo '  --test-env                       : Script to set environment variables for tests'
47     echo '  --crossgen                       : Precompiles the framework managed assemblies'
48     echo '  --runcrossgentests               : Runs the ready to run tests' 
49     echo '  --jitstress=<n>                  : Runs the tests with COMPlus_JitStress=n'
50     echo '  --jitstressregs=<n>              : Runs the tests with COMPlus_JitStressRegs=n'
51     echo '  --jitminopts                     : Runs the tests with COMPlus_JITMinOpts=1'
52     echo '  --jitforcerelocs                 : Runs the tests with COMPlus_ForceRelocs=1'
53     echo '  --jitdisasm                      : Runs jit-dasm on the tests'
54     echo '  --gcstresslevel=<n>              : Runs the tests with COMPlus_GCStress=n'
55     echo '  --ilasmroundtrip                 : Runs ilasm round trip on the tests'
56     echo '    0: None                                1: GC on all allocs and '"'easy'"' places'
57     echo '    2: GC on transitions to preemptive GC  4: GC on every allowable JITed instr'
58     echo '    8: GC on every allowable NGEN instr   16: GC only on a unique stack trace'
59     echo '  --long-gc                        : Runs the long GC tests'
60     echo '  --gcsimulator                    : Runs the GCSimulator tests'
61     echo '  --link <ILlink>                  : Runs the tests after linking via ILlink'
62     echo '  --show-time                      : Print execution sequence and running time for each test'
63     echo '  --no-lf-conversion               : Do not execute LF conversion before running test script'
64     echo '  --limitedDumpGeneration          : Enables the generation of a limited number of core dumps if test(s) crash, even if ulimit'
65     echo '                                     is zero when launching this script. This option is intended for use in CI.'
66     echo '  --xunitOutputPath=<path>         : Create xUnit XML report at the specifed path (default: <test root>/coreclrtests.xml)'
67     echo ''
68     echo 'Runtime Code Coverage options:'
69     echo '  --coreclr-coverage               : Optional argument to get coreclr code coverage reports'
70     echo '  --coreclr-objs=<path>            : Location of root of the object directory'
71     echo '                                     containing the linux/mac coreclr build'
72     echo '  --coreclr-src=<path>             : Location of root of the directory'
73     echo '                                     containing the coreclr source files'
74     echo '  --coverage-output-dir=<path>     : Directory where coverage output will be written to'
75     echo ''
76 }
77
78 function print_results {
79     echo ""
80     echo "======================="
81     echo "     Test Results"
82     echo "======================="
83     echo "# CoreCLR Bin Dir  : $coreClrBinDir"
84     echo "# Tests Discovered : $countTotalTests"
85     echo "# Passed           : $countPassedTests"
86     echo "# Failed           : $countFailedTests"
87     echo "# Skipped          : $countSkippedTests"
88     echo "======================="
89 }
90
91 # Initialize counters for bookkeeping.
92 countTotalTests=0
93 countPassedTests=0
94 countFailedTests=0
95 countSkippedTests=0
96
97 # Variables for xUnit-style XML output. XML format: https://xunit.github.io/docs/format-xml-v2.html
98 xunitOutputPath=
99 xunitTestOutputPath=
100
101 # libExtension determines extension for dynamic library files
102 # runtimeName determines where CoreFX Runtime files will be located
103 OSName=$(uname -s)
104 libExtension=
105 case $OSName in
106     Darwin)
107         libExtension="dylib"
108         ;;
109
110     Linux)
111         libExtension="so"
112         ;;
113
114     NetBSD)
115         libExtension="so"
116         ;;
117
118     *)
119         echo "Unsupported OS $OSName detected, configuring as if for Linux"
120         libExtension="so"
121         ;;
122 esac
123
124 function xunit_output_begin {
125     xunitOutputPath=$testRootDir/coreclrtests.xml
126     xunitTestOutputPath=${xunitOutputPath}.test
127     if [ -e "$xunitOutputPath" ]; then
128         rm -f -r "$xunitOutputPath"
129     fi
130     if [ -e "$xunitTestOutputPath" ]; then
131         rm -f -r "$xunitTestOutputPath"
132     fi
133 }
134
135 function xunit_output_add_test {
136     # <assemblies>
137     #   <assembly>
138     #     <collection>
139     #       <test .../> <!-- Write this element here -->
140
141     local scriptFilePath=$1
142     local outputFilePath=$2
143     local testResult=$3 # Pass, Fail, or Skip
144     local testScriptExitCode=$4
145
146     local testPath=${scriptFilePath%.sh} # Remove trailing ".sh"
147     local testDir=$(dirname "$testPath")
148     local testName=$(basename "$testPath")
149
150     # Replace '/' with '.'
151     testPath=$(echo "$testPath" | tr / .)
152     testDir=$(echo "$testDir" | tr / .)
153
154     local line
155
156     line="      "
157     line="${line}<test"
158     line="${line} name=\"${testPath}\""
159     line="${line} type=\"${testDir}\""
160     line="${line} method=\"${testName}\""
161     line="${line} result=\"${testResult}\""
162
163     if [ "$testResult" == "Pass" ]; then
164         line="${line}/>"
165         echo "$line" >>"$xunitTestOutputPath"
166         return
167     fi
168
169     line="${line}>"
170     echo "$line" >>"$xunitTestOutputPath"
171
172     line="        "
173     if [ "$testResult" == "Skip" ]; then
174         line="${line}<reason><![CDATA[$(cat "$outputFilePath")]]></reason>"
175         echo "$line" >>"$xunitTestOutputPath"
176     else
177         line="${line}<failure exception-type=\"Exit code: ${testScriptExitCode}\">"
178         echo "$line" >>"$xunitTestOutputPath"
179
180         line="          "
181         line="${line}<message>"
182         echo "$line" >>"$xunitTestOutputPath"
183         line="            "
184         line="${line}<![CDATA["
185         echo "$line" >>"$xunitTestOutputPath"
186         cat "$outputFilePath" >>"$xunitTestOutputPath"
187         line="            "
188         line="${line}]]>"
189         echo "$line" >>"$xunitTestOutputPath"
190         line="          "
191         line="${line}</message>"
192         echo "$line" >>"$xunitTestOutputPath"
193
194         line="        "
195         line="${line}</failure>"
196         echo "$line" >>"$xunitTestOutputPath"
197     fi
198
199     line="      "
200     line="${line}</test>"
201     echo "$line" >>"$xunitTestOutputPath"
202 }
203
204 function xunit_output_end {
205     local errorSource=$1
206     local errorMessage=$2
207
208     local errorCount
209     if [ -z "$errorSource" ]; then
210         ((errorCount = 0))
211     else
212         ((errorCount = 1))
213     fi
214
215     echo '<?xml version="1.0" encoding="utf-8"?>' >>"$xunitOutputPath"
216     echo '<assemblies>' >>"$xunitOutputPath"
217
218     local line
219
220     # <assembly ...>
221     line="  "
222     line="${line}<assembly"
223     line="${line} name=\"CoreClrTestAssembly\""
224     line="${line} total=\"${countTotalTests}\""
225     line="${line} passed=\"${countPassedTests}\""
226     line="${line} failed=\"${countFailedTests}\""
227     line="${line} skipped=\"${countSkippedTests}\""
228     line="${line} errors=\"${errorCount}\""
229     line="${line}>"
230     echo "$line" >>"$xunitOutputPath"
231
232     # <collection ...>
233     line="    "
234     line="${line}<collection"
235     line="${line} name=\"CoreClrTestCollection\""
236     line="${line} total=\"${countTotalTests}\""
237     line="${line} passed=\"${countPassedTests}\""
238     line="${line} failed=\"${countFailedTests}\""
239     line="${line} skipped=\"${countSkippedTests}\""
240     line="${line}>"
241     echo "$line" >>"$xunitOutputPath"
242
243     # <test .../> <test .../> ...
244     if [ -f "$xunitTestOutputPath" ]; then
245         cat "$xunitTestOutputPath" >>"$xunitOutputPath"
246         rm -f "$xunitTestOutputPath"
247     fi
248
249     # </collection>
250     line="    "
251     line="${line}</collection>"
252     echo "$line" >>"$xunitOutputPath"
253
254     if [ -n "$errorSource" ]; then
255         # <errors>
256         line="    "
257         line="${line}<errors>"
258         echo "$line" >>"$xunitOutputPath"
259
260         # <error ...>
261         line="      "
262         line="${line}<error"
263         line="${line} type=\"TestHarnessError\""
264         line="${line} name=\"${errorSource}\""
265         line="${line}>"
266         echo "$line" >>"$xunitOutputPath"
267
268         # <failure .../>
269         line="        "
270         line="${line}<failure>${errorMessage}</failure>"
271         echo "$line" >>"$xunitOutputPath"
272
273         # </error>
274         line="      "
275         line="${line}</error>"
276         echo "$line" >>"$xunitOutputPath"
277
278         # </errors>
279         line="    "
280         line="${line}</errors>"
281         echo "$line" >>"$xunitOutputPath"
282     fi
283
284     # </assembly>
285     line="  "
286     line="${line}</assembly>"
287     echo "$line" >>"$xunitOutputPath"
288
289     # </assemblies>
290     echo '</assemblies>' >>"$xunitOutputPath"
291 }
292
293 function exit_with_error {
294     local errorSource=$1
295     local errorMessage=$2
296     local printUsage=$3
297
298     if [ -z "$printUsage" ]; then
299         ((printUsage = 0))
300     fi
301
302     echo "$errorMessage"
303     xunit_output_end "$errorSource" "$errorMessage"
304     if ((printUsage != 0)); then
305         print_usage
306     fi
307     exit $EXIT_CODE_EXCEPTION
308 }
309
310 # Handle Ctrl-C. We will stop execution and print the results that
311 # we gathered so far.
312 function handle_ctrl_c {
313     local errorSource='handle_ctrl_c'
314
315     echo ""
316     echo "*** Stopping... ***"
317     print_results
318     exit_with_error "$errorSource" "Test run aborted by Ctrl+C."
319 }
320
321 # Register the Ctrl-C handler
322 trap handle_ctrl_c INT
323
324 function create_core_overlay {
325     local errorSource='create_core_overlay'
326     local printUsage=1
327
328     if [ -n "$coreOverlayDir" ]; then
329         export CORE_ROOT="$coreOverlayDir"
330         return
331     fi
332
333     # Check inputs to make sure we have enough information to create the core layout. $testRootDir/Tests/Core_Root should
334     # already exist and contain test dependencies that are not built.
335     local testDependenciesDir=$testRootDir/Tests/Core_Root
336     if [ ! -d "$testDependenciesDir" ]; then
337         exit_with_error "$errorSource" "Did not find the test dependencies directory: $testDependenciesDir"
338     fi
339     if [ -z "$coreClrBinDir" ]; then
340         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreClrBinDir must be specified." "$printUsage"
341     fi
342     if [ ! -d "$coreClrBinDir" ]; then
343         exit_with_error "$errorSource" "Directory specified by --coreClrBinDir does not exist: $coreClrBinDir"
344     fi
345     if [ -z "$coreFxBinDir" ]; then
346         exit_with_error "$errorSource" "One of --coreOverlayDir or --coreFxBinDir must be specified." "$printUsage"
347     fi
348
349     # Create the overlay
350     coreOverlayDir=$testRootDir/Tests/coreoverlay
351     export CORE_ROOT="$coreOverlayDir"
352     if [ -e "$coreOverlayDir" ]; then
353         rm -f -r "$coreOverlayDir"
354     fi
355     mkdir "$coreOverlayDir"
356
357     cp -f -v "$coreFxBinDir/"* "$coreOverlayDir/" 2>/dev/null
358     cp -f -p -v "$coreClrBinDir/"* "$coreOverlayDir/" 2>/dev/null
359     if [ -d "$mscorlibDir/bin" ]; then
360         cp -f -v "$mscorlibDir/bin/"* "$coreOverlayDir/" 2>/dev/null
361     fi
362     cp -f -v "$testDependenciesDir/"xunit* "$coreOverlayDir/" 2>/dev/null
363     cp -n -v "$testDependenciesDir/"* "$coreOverlayDir/" 2>/dev/null
364     if [ -f "$coreOverlayDir/mscorlib.ni.dll" ]; then
365         # Test dependencies come from a Windows build, and mscorlib.ni.dll would be the one from Windows
366         rm -f "$coreOverlayDir/mscorlib.ni.dll"
367     fi
368     if [ -f "$coreOverlayDir/System.Private.CoreLib.ni.dll" ]; then
369         # Test dependencies come from a Windows build, and System.Private.CoreLib.ni.dll would be the one from Windows
370         rm -f "$coreOverlayDir/System.Private.CoreLib.ni.dll"
371     fi
372     copy_test_native_bin_to_test_root
373 }
374
375 declare -a skipCrossGenFiles
376
377 function is_skip_crossgen_test {
378     for skip in "${skipCrossGenFiles[@]}"; do
379         if [ "$1" == "$skip" ]; then
380             return 0
381         fi
382     done
383     return 1
384 }
385
386 function precompile_overlay_assemblies {
387     skipCrossGenFiles=($(read_array "$(dirname "$0")/skipCrossGenFiles.$ARCH.txt"))
388
389     if [ $doCrossgen == 1 ]; then
390         local overlayDir=$CORE_ROOT
391
392         filesToPrecompile=$(find -L $overlayDir -iname \*.dll -not -iname \*.ni.dll -not -iname \*-ms-win-\* -type f )
393         for fileToPrecompile in ${filesToPrecompile}
394         do
395             local filename=${fileToPrecompile}
396             if [ $jitdisasm == 1 ]; then
397                 $overlayDir/corerun $overlayDir/jit-dasm.dll --crossgen $overlayDir/crossgen --platform $overlayDir --output $testRootDir/dasm $filename
398                 local exitCode=$?
399                 if [ $exitCode != 0 ]; then
400                     echo Unable to generate dasm for $filename
401                 fi
402             else
403                 if is_skip_crossgen_test "$(basename $filename)"; then
404                     continue
405                 fi
406                 echo Precompiling $filename
407                 $overlayDir/crossgen /Platform_Assemblies_Paths $overlayDir $filename 1> $filename.stdout 2>$filename.stderr
408                 local exitCode=$?
409                 if [[ $exitCode != 0 ]]; then
410                     if grep -q -e '0x80131018' $filename.stderr; then
411                         printf "\n\t$filename is not a managed assembly.\n\n"
412                     else
413                         echo Unable to precompile $filename.
414                         cat $filename.stdout
415                         cat $filename.stderr
416                         exit $exitCode
417                     fi
418                 else
419                     rm $filename.{stdout,stderr}
420                 fi
421             fi
422         done
423     else
424         echo Skipping crossgen of FX assemblies.
425     fi
426 }
427
428 function copy_test_native_bin_to_test_root {
429     local errorSource='copy_test_native_bin_to_test_root'
430
431     if [ -z "$testNativeBinDir" ]; then
432         exit_with_error "$errorSource" "--testNativeBinDir is required."
433     fi
434     testNativeBinDir=$testNativeBinDir/src
435     if [ ! -d "$testNativeBinDir" ]; then
436         exit_with_error "$errorSource" "Directory specified by --testNativeBinDir does not exist: $testNativeBinDir"
437     fi
438
439     # Copy native test components from the native test build into the respective test directory in the test root directory
440     find "$testNativeBinDir" -type f -iname '*.$libExtension' |
441         while IFS='' read -r filePath || [ -n "$filePath" ]; do
442             local dirPath=$(dirname "$filePath")
443             local destinationDirPath=${testRootDir}${dirPath:${#testNativeBinDir}}
444             if [ ! -d "$destinationDirPath" ]; then
445                 exit_with_error "$errorSource" "Cannot copy native test bin '$filePath' to '$destinationDirPath/', as the destination directory does not exist."
446             fi
447             cp -f "$filePath" "$destinationDirPath/"
448         done
449 }
450
451 # Variables for unsupported and failing tests
452 declare -a unsupportedTests
453 declare -a failingTests
454 declare -a playlistTests
455 ((runFailingTestsOnly = 0))
456
457 # Get an array of items by reading the specified file line by line.
458 function read_array {
459     local theArray=()
460
461     # bash in Mac OS X doesn't support 'readarray', so using alternate way instead.
462     # readarray -t theArray < "$1"
463     while IFS='' read -r line || [ -n "$line" ]; do
464         theArray[${#theArray[@]}]=$line
465     done < "$1"
466     echo ${theArray[@]}
467 }
468
469 function load_unsupported_tests {
470     # Load the list of tests that are not supported on this platform. These tests are disabled (skipped) permanently.
471     unsupportedTests=($(read_array "$(dirname "$0")/testsUnsupportedOutsideWindows.txt"))
472     if [ "$ARCH" == "arm" ]; then
473         unsupportedTests+=($(read_array "$(dirname "$0")/testsUnsupportedOnARM32.txt"))
474     fi
475 }
476
477 function load_failing_tests {
478     # Load the list of tests that fail on this platform. These tests are disabled (skipped) temporarily, pending investigation.
479     failingTests=($(read_array "$(dirname "$0")/testsFailingOutsideWindows.txt"))
480    
481     if [ "$ARCH" == "arm64" ]; then
482         failingTests+=($(read_array "$(dirname "$0")/testsFailingOnArm64.txt"))
483     fi
484 }
485
486 function load_playlist_tests {
487     # Load the list of tests that are enabled as a part of this test playlist.
488     playlistTests=($(read_array "${playlistFile}"))
489 }
490
491 function is_unsupported_test {
492     for unsupportedTest in "${unsupportedTests[@]}"; do
493         if [ "$1" == "$unsupportedTest" ]; then
494             return 0
495         fi
496     done
497     return 1
498 }
499
500 function is_failing_test {
501     for failingTest in "${failingTests[@]}"; do
502         if [ "$1" == "$failingTest" ]; then
503             return 0
504         fi
505     done
506     return 1
507 }
508
509 function is_playlist_test {
510     for playlistTest in "${playlistTests[@]}"; do
511         if [ "$1" == "$playlistTest" ]; then
512             return 0
513         fi
514     done
515     return 1
516 }
517
518 function skip_unsupported_test {
519     # This function runs in a background process. It should not echo anything, and should not use global variables. This
520     # function is analogous to run_test, and causes the test to be skipped with the message below.
521
522     local scriptFilePath=$1
523     local outputFilePath=$2
524
525     echo "Not supported on this platform." >"$outputFilePath"
526     return 2 # skip the test
527 }
528
529 function skip_failing_test {
530     # This function runs in a background process. It should not echo anything, and should not use global variables. This
531     # function is analogous to run_test, and causes the test to be skipped with the message below.
532
533     local scriptFilePath=$1
534     local outputFilePath=$2
535
536     echo "Temporarily disabled on this platform due to unexpected failures." >"$outputFilePath"
537     return 2 # skip the test
538 }
539
540 function skip_non_playlist_test {
541     # This function runs in a background process. It should not echo anything, and should not use global variables. This
542     # function is analogous to run_test, and causes the test to be skipped with the message below.
543
544     local scriptFilePath=$1
545     local outputFilePath=$2
546
547     echo "Test is not included in the running playlist." >"$outputFilePath"
548     return 2 # skip the test
549 }
550
551 function set_up_core_dump_generation {
552     # We will only enable dump generation here if we're on Mac or Linux
553     if [[ ! ( "$(uname -s)" == "Darwin" || "$(uname -s)" == "Linux" ) ]]; then
554         return
555     fi
556
557     # We won't enable dump generation on OS X/macOS if the machine hasn't been
558     # configured with the kern.corefile pattern we expect.
559     if [[ ( "$(uname -s)" == "Darwin" && "$(sysctl -n kern.corefile)" != "core.%P" ) ]]; then
560         echo "WARNING: Core dump generation not being enabled due to unexpected kern.corefile value."
561         return
562     fi
563
564     # Allow dump generation
565     ulimit -c unlimited
566
567     if [ "$(uname -s)" == "Linux" ]; then
568         if [ -e /proc/self/coredump_filter ]; then
569             # Include memory in private and shared file-backed mappings in the dump.
570             # This ensures that we can see disassembly from our shared libraries when
571             # inspecting the contents of the dump. See 'man core' for details.
572             echo 0x3F > /proc/self/coredump_filter
573         fi
574     fi
575 }
576
577 function print_info_from_core_file {
578
579     #### temporary
580     if [ "$ARCH" == "arm64" ]; then
581         echo "Not inspecting core dumps on arm64 at the moment."
582         return
583     fi
584     ####
585
586     local core_file_name=$1
587     local executable_name=$2
588
589     if ! [ -e $executable_name ]; then
590         echo "Unable to find executable $executable_name"
591         return
592     elif ! [ -e $core_file_name ]; then
593         echo "Unable to find core file $core_file_name"
594         return
595     fi
596
597     # Use LLDB to inspect the core dump on Mac, and GDB everywhere else.
598     if [[ "$OSName" == "Darwin" ]]; then
599         hash lldb 2>/dev/null || { echo >&2 "LLDB was not found. Unable to print core file."; return; }
600
601         echo "Printing info from core file $core_file_name"
602         lldb -c $core_file_name -b -o 'bt'
603     else
604         # Use GDB to print the backtrace from the core file.
605         hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; }
606
607         echo "Printing info from core file $core_file_name"
608         gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name
609     fi
610 }
611
612 function download_dumpling_script {
613     echo "Downloading latest version of dumpling script."
614     wget "https://dumpling.azurewebsites.net/api/client/dumpling.py"
615
616     local dumpling_script="dumpling.py"
617     chmod +x $dumpling_script
618 }
619
620 function upload_core_file_to_dumpling {
621     local core_file_name=$1
622     local dumpling_script="dumpling.py"
623     local dumpling_file="local_dumplings.txt"
624
625     # dumpling requires that the file exist before appending.
626     touch ./$dumpling_file
627
628     if [ ! -x $dumpling_script ]; then
629         download_dumpling_script
630     fi
631
632     if [ ! -x $dumpling_script ]; then
633         echo "Failed to download dumpling script. Dump cannot be uploaded."
634         return
635     fi
636
637     echo "Uploading $core_file_name to dumpling service."
638
639     local paths_to_add=""
640     if [ -d "$coreClrBinDir" ]; then
641         echo "Uploading CoreCLR binaries with dump."
642         paths_to_add=$coreClrBinDir
643     fi
644
645     # Ensure the script has Unix line endings
646     perl -pi -e 's/\r\n|\n|\r/\n/g' "$dumpling_script"
647
648     # The output from this will include a unique ID for this dump.
649     ./$dumpling_script "upload" "--dumppath" "$core_file_name" "--incpaths" $paths_to_add "--properties" "Project=CoreCLR" "--squelch" | tee -a $dumpling_file
650 }
651
652 function preserve_core_file {
653     local core_file_name=$1
654     local storage_location="/tmp/coredumps_coreclr"
655
656     # Create the directory (this shouldn't fail even if it already exists).
657     mkdir -p $storage_location
658
659     # Only preserve the dump if the directory is empty. Otherwise, do nothing.
660     # This is a way to prevent us from storing/uploading too many dumps.
661     if [ ! "$(ls -A $storage_location)" ]; then
662         echo "Copying core file $core_file_name to $storage_location"
663         cp $core_file_name $storage_location
664
665         upload_core_file_to_dumpling $core_file_name
666     fi
667 }
668
669 function inspect_and_delete_core_files {
670     # This function prints some basic information from core files in the current
671     # directory and deletes them immediately. Based on the state of the system, it may
672     # also upload a core file to the dumpling service.
673     # (see preserve_core_file).
674     
675     # Depending on distro/configuration, the core files may either be named "core"
676     # or "core.<PID>" by default. We will read /proc/sys/kernel/core_uses_pid to 
677     # determine which one it is.
678     # On OS X/macOS, we checked the kern.corefile value before enabling core dump
679     # generation, so we know it always includes the PID.
680     local core_name_uses_pid=0
681     if [[ (( -e /proc/sys/kernel/core_uses_pid ) && ( "1" == $(cat /proc/sys/kernel/core_uses_pid) )) 
682           || ( "$(uname -s)" == "Darwin" ) ]]; then
683         core_name_uses_pid=1
684     fi
685
686     if [ $core_name_uses_pid == "1" ]; then
687         # We don't know what the PID of the process was, so let's look at all core
688         # files whose name matches core.NUMBER
689         for f in core.*; do
690             [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" $CORE_ROOT/"corerun" && preserve_core_file "$f" && rm "$f"
691         done
692     elif [ -f core ]; then
693         print_info_from_core_file "core" $CORE_ROOT/"corerun"
694         preserve_core_file "core"
695         rm "core"
696     fi
697 }
698
699 function run_test {
700     # This function runs in a background process. It should not echo anything, and should not use global variables.
701
702     local scriptFilePath=$1
703     local outputFilePath=$2
704
705     # Switch to directory where the script is
706     cd "$(dirname "$scriptFilePath")"
707
708     local scriptFileName=$(basename "$scriptFilePath")
709     local outputFileName=$(basename "$outputFilePath")
710
711     if [ "$limitedCoreDumps" == "ON" ]; then
712         set_up_core_dump_generation
713     fi
714
715     "./$scriptFileName" >"$outputFileName" 2>&1
716     local testScriptExitCode=$?
717
718     # We will try to print some information from generated core dumps if a debugger
719     # is available, and possibly store a dump in a non-transient location.
720     if [ "$limitedCoreDumps" == "ON" ]; then
721         inspect_and_delete_core_files
722     fi
723
724     return $testScriptExitCode
725 }
726
727 # Variables for running tests in the background
728 if [ `uname` = "NetBSD" ]; then
729     NumProc=$(getconf NPROCESSORS_ONLN)
730 else
731     NumProc=$(getconf _NPROCESSORS_ONLN)
732 fi
733 ((maxProcesses = $NumProc * 3 / 2)) # long tests delay process creation, use a few more processors
734
735 ((nextProcessIndex = 0))
736 ((processCount = 0))
737 declare -a scriptFilePaths
738 declare -a outputFilePaths
739 declare -a processIds
740 declare -a testStartTimes
741
742 function finish_test {
743     wait ${processIds[$nextProcessIndex]}
744     local testScriptExitCode=$?
745     ((--processCount))
746
747     local scriptFilePath=${scriptFilePaths[$nextProcessIndex]}
748     local outputFilePath=${outputFilePaths[$nextProcessIndex]}
749     local scriptFileName=$(basename "$scriptFilePath")
750
751     local testEndTime=
752     local testRunningTime=
753     local header=
754
755     if ((verbose == 1)); then
756         header=$(printf "[%4d]" $countTotalTests)
757     fi
758
759     if [ "$showTime" == "ON" ]; then
760         testEndTime=$(date +%s)
761         testRunningTime=$(( $testEndTime - ${testStartTimes[$nextProcessIndex]} ))
762         header=$header$(printf "[%4ds]" $testRunningTime)
763     fi
764
765     local xunitTestResult
766     case $testScriptExitCode in
767         0)
768             let countPassedTests++
769             xunitTestResult='Pass'
770             if ((verbose == 1 || runFailingTestsOnly == 1)); then
771                 echo "PASSED   - ${header}${scriptFilePath}"
772             else
773                 echo "         - ${header}${scriptFilePath}"
774             fi
775             ;;
776         2)
777             let countSkippedTests++
778             xunitTestResult='Skip'
779             echo "SKIPPED  - ${header}${scriptFilePath}"
780             ;;
781         *)
782             let countFailedTests++
783             xunitTestResult='Fail'
784             echo "FAILED   - ${header}${scriptFilePath}"
785             ;;
786     esac
787     let countTotalTests++
788
789     if ((verbose == 1 || testScriptExitCode != 0)); then
790         while IFS='' read -r line || [ -n "$line" ]; do
791             echo "               $line"
792         done <"$outputFilePath"
793     fi
794
795     xunit_output_add_test "$scriptFilePath" "$outputFilePath" "$xunitTestResult" "$testScriptExitCode"
796 }
797
798 function finish_remaining_tests {
799     # Finish the remaining tests in the order in which they were started
800     if ((nextProcessIndex >= processCount)); then
801         ((nextProcessIndex = 0))
802     fi
803     while ((processCount > 0)); do
804         finish_test
805         ((nextProcessIndex = (nextProcessIndex + 1) % maxProcesses))
806     done
807     ((nextProcessIndex = 0))
808 }
809
810 function prep_test {
811     local scriptFilePath=$1
812     local scriptFileDir=$(dirname "$scriptFilePath")
813
814     test "$verbose" == 1 && echo "Preparing $scriptFilePath"
815
816     if [ ! "$noLFConversion" == "ON" ]; then
817         # Convert DOS line endings to Unix if needed
818         perl -pi -e 's/\r\n|\n|\r/\n/g' "$scriptFilePath"
819     fi
820         
821     # Add executable file mode bit if needed
822     chmod +x "$scriptFilePath"
823
824     #remove any NI and Locks
825     rm -f $scriptFileDir/*.ni.*
826     rm -rf $scriptFileDir/lock
827 }
828
829 function start_test {
830     local scriptFilePath=$1
831     if ((runFailingTestsOnly == 1)) && ! is_failing_test "$scriptFilePath"; then
832         return
833     fi
834
835     # Skip any test that's not in the current playlist, if a playlist was
836     # given to us.
837     if [ -n "$playlistFile" ] && ! is_playlist_test "$scriptFilePath"; then
838         return
839     fi
840
841     if ((nextProcessIndex < processCount)); then
842         finish_test
843     fi
844
845     scriptFilePaths[$nextProcessIndex]=$scriptFilePath
846     local scriptFileName=$(basename "$scriptFilePath")
847     local outputFilePath=$(dirname "$scriptFilePath")/${scriptFileName}.out
848     outputFilePaths[$nextProcessIndex]=$outputFilePath
849
850     if [ "$showTime" == "ON" ]; then
851         testStartTimes[$nextProcessIndex]=$(date +%s)
852     fi
853
854     test "$verbose" == 1 && echo "Starting $scriptFilePath"
855     if is_unsupported_test "$scriptFilePath"; then
856         skip_unsupported_test "$scriptFilePath" "$outputFilePath" &
857     elif ((runFailingTestsOnly == 0)) && is_failing_test "$scriptFilePath"; then
858         skip_failing_test "$scriptFilePath" "$outputFilePath" &
859     else
860         run_test "$scriptFilePath" "$outputFilePath" &
861     fi
862     processIds[$nextProcessIndex]=$!
863
864     ((nextProcessIndex = (nextProcessIndex + 1) % maxProcesses))
865     ((++processCount))
866 }
867
868 # Get a list of directories in which to scan for tests by reading the
869 # specified file line by line.
870 function set_test_directories {
871     local errorSource='set_test_directories'
872
873     local listFileName=$1
874
875     if [ ! -f "$listFileName" ]
876     then
877         exit_with_error "$errorSource" "Test directories file not found at $listFileName"
878     fi
879     testDirectories=($(read_array "$listFileName"))
880 }
881
882 function run_tests_in_directory {
883     local testDir=$1
884
885     # Recursively search through directories for .sh files to prepare them.
886     # Note: This needs to occur before any test runs as some of the .sh files
887     # depend on other .sh files
888     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
889     do
890         prep_test "${scriptFilePath:2}"
891     done
892     echo "The tests have been prepared"
893     # Recursively search through directories for .sh files to run.
894     for scriptFilePath in $(find "$testDir" -type f -iname '*.sh' | sort)
895     do
896         start_test "${scriptFilePath:2}"
897     done
898 }
899
900 function coreclr_code_coverage {
901     local coverageDir="$coverageOutputDir/Coverage"
902     local toolsDir="$coverageOutputDir/Coverage/tools"
903     local reportsDir="$coverageOutputDir/Coverage/reports"
904     local packageName="unix-code-coverage-tools.1.0.0.nupkg"
905
906     rm -rf $coverageDir
907     mkdir -p $coverageDir
908     mkdir -p $toolsDir
909     mkdir -p $reportsDir
910     pushd $toolsDir > /dev/null
911
912     echo "Pulling down code coverage tools"
913     wget -q https://www.myget.org/F/dotnet-buildtools/api/v2/package/unix-code-coverage-tools/1.0.0 -O $packageName
914     echo "Unzipping to $toolsDir"
915     unzip -q -o $packageName
916
917     # Invoke gcovr
918     chmod a+rwx ./gcovr
919     chmod a+rwx ./$OSName/llvm-cov
920
921     echo
922     echo "Generating coreclr code coverage reports at $reportsDir/coreclr.html"
923     echo "./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OS/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html"
924     echo
925     ./gcovr $coreClrObjs --gcov-executable=$toolsDir/$OSName/llvm-cov -r $coreClrSrc --html --html-details -o $reportsDir/coreclr.html
926     exitCode=$?
927     popd > /dev/null
928     exit $exitCode
929 }
930
931 function check_cpu_architecture {
932     local CPUName=$(uname -m)
933     local __arch=
934
935     case $CPUName in
936         i686)
937             __arch=x86
938             ;;
939         x86_64)
940             __arch=x64
941             ;;
942         armv7l)
943             __arch=arm
944             ;;
945         aarch64)
946             __arch=arm64
947             ;;
948         *)
949             echo "Unknown CPU $CPUName detected, configuring as if for x64"
950             __arch=x64
951             ;;
952     esac
953
954     echo "$__arch"
955 }
956
957 ARCH=$(check_cpu_architecture)
958 echo "Running on  CPU- $ARCH"
959
960 # Exit code constants
961 readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
962 readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
963 readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.
964
965 # Argument variables
966 testRootDir=
967 testNativeBinDir=
968 coreOverlayDir=
969 coreClrBinDir=
970 mscorlibDir=
971 coreFxBinDir=
972 coreClrObjs=
973 coreClrSrc=
974 coverageOutputDir=
975 testEnv=
976 playlistFile=
977 showTime=
978 noLFConversion=
979 buildOverlayOnly=
980 gcsimulator=
981 longgc=
982 limitedCoreDumps=
983 illinker=
984 ((disableEventLogging = 0))
985 ((serverGC = 0))
986
987 # Handle arguments
988 verbose=0
989 doCrossgen=0
990 jitdisasm=0
991 ilasmroundtrip=
992
993 for i in "$@"
994 do
995     case $i in
996         -h|--help)
997             print_usage
998             exit $EXIT_CODE_SUCCESS
999             ;;
1000         -v|--verbose)
1001             verbose=1
1002             ;;
1003         --crossgen)
1004             doCrossgen=1
1005             ;;
1006         --jitstress=*)
1007             export COMPlus_JitStress=${i#*=}
1008             ;;
1009         --jitstressregs=*)
1010             export COMPlus_JitStressRegs=${i#*=}
1011             ;;
1012         --jitminopts)
1013             export COMPlus_JITMinOpts=1
1014             ;;
1015         --jitforcerelocs)
1016             export COMPlus_ForceRelocs=1
1017             ;;
1018         --link=*)
1019             export ILLINK=${i#*=}
1020             export DoLink=true
1021             ;;
1022         --jitdisasm)
1023             jitdisasm=1
1024             ;;
1025         --ilasmroundtrip)
1026             ((ilasmroundtrip = 1))
1027             ;;
1028         --testRootDir=*)
1029             testRootDir=${i#*=}
1030             ;;
1031         --testNativeBinDir=*)
1032             testNativeBinDir=${i#*=}
1033             ;;
1034         --coreOverlayDir=*)
1035             coreOverlayDir=${i#*=}
1036             ;;
1037         --coreClrBinDir=*)
1038             coreClrBinDir=${i#*=}
1039             ;;
1040         --mscorlibDir=*)
1041             mscorlibDir=${i#*=}
1042             ;;
1043         --coreFxBinDir=*)
1044             coreFxBinDir=${i#*=}
1045             ;;
1046         --testDir=*)
1047             testDirectories[${#testDirectories[@]}]=${i#*=}
1048             ;;
1049         --testDirFile=*)
1050             set_test_directories "${i#*=}"
1051             ;;
1052         --runFailingTestsOnly)
1053             ((runFailingTestsOnly = 1))
1054             ;;
1055         --disableEventLogging)
1056             ((disableEventLogging = 1))
1057             ;;
1058         --runcrossgentests)
1059             export RunCrossGen=1
1060             ;;
1061         --sequential)
1062             ((maxProcesses = 1))
1063             ;;
1064         --useServerGC)
1065             ((serverGC = 1))
1066             ;;
1067         --long-gc)
1068             ((longgc = 1))
1069             ;;
1070         --gcsimulator)
1071             ((gcsimulator = 1))
1072             ;;
1073         --playlist=*)
1074             playlistFile=${i#*=}
1075             ;;
1076         --coreclr-coverage)
1077             CoreClrCoverage=ON
1078             ;;
1079         --coreclr-objs=*)
1080             coreClrObjs=${i#*=}
1081             ;;
1082         --coreclr-src=*)
1083             coreClrSrc=${i#*=}
1084             ;;
1085         --coverage-output-dir=*)
1086             coverageOutputDir=${i#*=}
1087             ;;
1088         --test-env=*)
1089             testEnv=${i#*=}
1090             ;;            
1091         --gcstresslevel=*)
1092             export COMPlus_GCStress=${i#*=}
1093             ;;            
1094         --show-time)
1095             showTime=ON
1096             ;;
1097         --no-lf-conversion)
1098             noLFConversion=ON
1099             ;;
1100         --build-overlay-only)
1101             buildOverlayOnly=ON
1102             ;;
1103         --limitedDumpGeneration)
1104             limitedCoreDumps=ON
1105             ;;
1106         --xunitOutputPath=*)
1107             xunitOutputPath=${i#*=}
1108             ;;
1109         *)
1110             echo "Unknown switch: $i"
1111             print_usage
1112             exit $EXIT_CODE_SUCCESS
1113             ;;
1114     esac
1115 done
1116
1117 if [ -n "$coreOverlayDir" ] && [ "$buildOverlayOnly" == "ON" ]; then
1118     echo "Can not use \'--coreOverlayDir=<path>\' and \'--build-overlay-only\' at the same time."
1119     exit $EXIT_CODE_EXCEPTION
1120 fi
1121
1122 if ((disableEventLogging == 0)); then
1123     export COMPlus_EnableEventLog=1
1124 fi
1125
1126 export CORECLR_SERVER_GC="$serverGC"
1127
1128 if [ -z "$testRootDir" ]; then
1129     echo "--testRootDir is required."
1130     print_usage
1131     exit $EXIT_CODE_EXCEPTION
1132 fi
1133 if [ ! -d "$testRootDir" ]; then
1134     echo "Directory specified by --testRootDir does not exist: $testRootDir"
1135     exit $EXIT_CODE_EXCEPTION
1136 fi
1137
1138 # Copy native interop test libraries over to the mscorlib path in
1139 # order for interop tests to run on linux.
1140 if [ -z "$mscorlibDir" ]; then
1141     mscorlibDir=$coreClrBinDir
1142 fi
1143
1144 if [ ! -z "$longgc" ]; then
1145     echo "Running Long GC tests"
1146     export RunningLongGCTests=1
1147 fi
1148
1149 if [ ! -z "$gcsimulator" ]; then
1150     echo "Running GC simulator tests"
1151     export RunningGCSimulatorTests=1
1152 fi
1153
1154 if [[ ! "$jitdisasm" -eq 0 ]]; then
1155     echo "Running jit disasm"
1156     export RunningJitDisasm=1
1157 fi
1158
1159 if [ ! -z "$ilasmroundtrip" ]; then
1160     echo "Running Ilasm round trip"
1161     export RunningIlasmRoundTrip=1
1162 fi
1163
1164 # If this is a coverage run, make sure the appropriate args have been passed
1165 if [ "$CoreClrCoverage" == "ON" ]
1166 then
1167     echo "Code coverage is enabled for this run"
1168     echo ""
1169     if [ ! "$OSName" == "Darwin" ] && [ ! "$OSName" == "Linux" ]
1170     then
1171         echo "Code Coverage not supported on $OS"
1172         exit 1
1173     fi
1174
1175     if [ -z "$coreClrObjs" ]
1176     then
1177         echo "Coreclr obj files are required to generate code coverage reports"
1178         echo "Coreclr obj files root path can be passed using '--coreclr-obj' argument"
1179         exit 1
1180     fi
1181
1182     if [ -z "$coreClrSrc" ]
1183     then
1184         echo "Coreclr src files are required to generate code coverage reports"
1185         echo "Coreclr src files root path can be passed using '--coreclr-src' argument"
1186         exit 1
1187     fi
1188
1189     if [ -z "$coverageOutputDir" ]
1190     then
1191         echo "Output directory for coverage results must be specified"
1192         echo "Output path can be specified '--coverage-output-dir' argument"
1193         exit 1
1194     fi
1195 fi
1196
1197 xunit_output_begin
1198 create_core_overlay
1199 precompile_overlay_assemblies
1200
1201 if [ "$buildOverlayOnly" == "ON" ];
1202 then
1203     echo "Build overlay directory \'$coreOverlayDir\' complete."
1204     exit 0
1205 fi
1206
1207 if [ -n "$playlistFile" ]
1208 then
1209     # Use a playlist file exclusively, if it was provided
1210     echo "Executing playlist $playlistFile"
1211     load_playlist_tests
1212 else
1213     load_unsupported_tests
1214     load_failing_tests
1215 fi
1216
1217 # Other architectures are not supported yet.
1218 if [ "$ARCH" == "x64" ]
1219 then
1220     scriptPath=$(dirname $0)
1221     ${scriptPath}/setup-stress-dependencies.sh --outputDir=$coreOverlayDir
1222 else
1223     if [ "$ARCH" != "arm64" ]
1224     then
1225         echo "Skip preparing for GC stress test. Dependent package is not supported on this architecture."
1226     fi
1227 fi
1228
1229 export __TestEnv=$testEnv
1230
1231 cd "$testRootDir"
1232
1233 dumplingsListPath="$testRootDir/dumplings.txt"
1234
1235 # clean up any existing dumpling remnants from previous runs.
1236 rm -f "$dumplingsListPath"
1237 find $testRootDir -type f -name "local_dumplings.txt" -exec rm {} \;
1238
1239 time_start=$(date +"%s")
1240 if [ -z "$testDirectories" ]
1241 then
1242     # No test directories were specified, so run everything in the current
1243     # directory and its subdirectories.
1244     run_tests_in_directory "."
1245 else
1246     # Otherwise, run all the tests in each specified test directory.
1247     for testDir in "${testDirectories[@]}"
1248     do
1249         if [ ! -d "$testDir" ]; then
1250             echo "Test directory does not exist: $testDir"
1251         else
1252             run_tests_in_directory "./$testDir"
1253         fi
1254     done
1255 fi
1256 finish_remaining_tests
1257
1258 print_results
1259
1260 find $testRootDir -type f -name "local_dumplings.txt" -exec cat {} \; > $dumplingsListPath
1261
1262 if [ -s $dumplingsListPath ]; then
1263     cat $dumplingsListPath
1264 else
1265     rm $dumplingsListPath
1266 fi
1267
1268 time_end=$(date +"%s")
1269 time_diff=$(($time_end-$time_start))
1270 echo "$(($time_diff / 60)) minutes and $(($time_diff % 60)) seconds taken to run CoreCLR tests."
1271
1272 xunit_output_end
1273
1274 if [ "$CoreClrCoverage" == "ON" ]
1275 then
1276     coreclr_code_coverage
1277 fi
1278
1279 if ((countFailedTests > 0)); then
1280     exit $EXIT_CODE_TEST_FAILURE
1281 fi
1282
1283 exit $EXIT_CODE_SUCCESS