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