|
| 1 | +#!/usr/bin/env bash |
| 2 | + |
| 3 | +usage() |
| 4 | +{ |
| 5 | + echo "Usage: RunTests.sh {-r|--runtime-path} <runtime-path> [{--rsp-file} <rsp-file>]" |
| 6 | + echo "" |
| 7 | + echo "Parameters:" |
| 8 | + echo "--runtime-path (Mandatory) Testhost containing the test runtime used during test execution (short: -r)" |
| 9 | + echo "--rsp-file RSP file to pass in additional arguments" |
| 10 | + echo "--help Print help and exit (short: -h)" |
| 11 | +} |
| 12 | + |
| 13 | +EXECUTION_DIR=$(dirname "$0") |
| 14 | +RUNTIME_PATH='' |
| 15 | +RSP_FILE='' |
| 16 | + |
| 17 | +while [[ $# > 0 ]]; do |
| 18 | + opt="$(echo "${1}" | awk '{print tolower($0)}')" |
| 19 | + case "$opt" in |
| 20 | + --help|-h) |
| 21 | + usage |
| 22 | + exit 0 |
| 23 | + ;; |
| 24 | + --runtime-path|-r) |
| 25 | + RUNTIME_PATH=$2 |
| 26 | + shift |
| 27 | + ;; |
| 28 | + --rsp-file) |
| 29 | + RSP_FILE=\@$2 |
| 30 | + shift |
| 31 | + ;; |
| 32 | + *) |
| 33 | + echo "Invalid argument: $1" |
| 34 | + usage |
| 35 | + exit -1 |
| 36 | + ;; |
| 37 | + esac |
| 38 | + |
| 39 | + shift |
| 40 | +done |
| 41 | + |
| 42 | +if [ "$RUNTIME_PATH" == "" ]; then |
| 43 | + echo "error: -r|--runtime-path argument is required." |
| 44 | + usage |
| 45 | + exit -1 |
| 46 | +fi |
| 47 | + |
| 48 | +# Don't use a globally installed SDK. |
| 49 | +export DOTNET_MULTILEVEL_LOOKUP=0 |
| 50 | + |
| 51 | +exitcode_list[0]="Exited Successfully" |
| 52 | +exitcode_list[130]="SIGINT Ctrl-C occurred. Likely tests timed out." |
| 53 | +exitcode_list[131]="SIGQUIT Ctrl-\ occurred. Core dumped." |
| 54 | +exitcode_list[132]="SIGILL Illegal Instruction. Core dumped. Likely codegen issue." |
| 55 | +exitcode_list[133]="SIGTRAP Breakpoint hit. Core dumped." |
| 56 | +exitcode_list[134]="SIGABRT Abort. Managed or native assert, or runtime check such as heap corruption, caused call to abort(). Core dumped." |
| 57 | +exitcode_list[135]="IGBUS Unaligned memory access. Core dumped." |
| 58 | +exitcode_list[136]="SIGFPE Bad floating point arguments. Core dumped." |
| 59 | +exitcode_list[137]="SIGKILL Killed eg by kill" |
| 60 | +exitcode_list[139]="SIGSEGV Illegal memory access. Deref invalid pointer, overrunning buffer, stack overflow etc. Core dumped." |
| 61 | +exitcode_list[143]="SIGTERM Terminated. Usually before SIGKILL." |
| 62 | +exitcode_list[159]="SIGSYS Bad System Call." |
| 63 | + |
| 64 | +function print_info_from_core_file_using_lldb { |
| 65 | + local core_file_name=$1 |
| 66 | + local executable_name=$2 |
| 67 | + local plugin_path_name="$RUNTIME_PATH/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so" |
| 68 | + |
| 69 | + # check for existence of lldb on the path |
| 70 | + hash lldb 2>/dev/null || { echo >&2 "lldb was not found. Unable to print core file."; return; } |
| 71 | + |
| 72 | + # pe, clrstack, and dumpasync are defined in libsosplugin.so |
| 73 | + if [ ! -f $plugin_path_name ]; then |
| 74 | + echo $plugin_path_name cannot be found. |
| 75 | + return |
| 76 | + fi |
| 77 | + |
| 78 | + echo ----- start =============== lldb Output ===================================================== |
| 79 | + echo Printing managed exceptions, managed call stacks, and async state machines. |
| 80 | + lldb -O "settings set target.exec-search-paths $RUNTIME_PATH" -o "plugin load $plugin_path_name" -o "clrthreads -managedexception" -o "pe -nested" -o "clrstack -all -a -f" -o "dumpasync -fields -stacks -roots" -o "quit" --core $core_file_name $executable_name |
| 81 | + echo ----- end =============== lldb Output ======================================================= |
| 82 | +} |
| 83 | + |
| 84 | +function print_info_from_core_file_using_gdb { |
| 85 | + local core_file_name=$1 |
| 86 | + local executable_name=$2 |
| 87 | + |
| 88 | + # Check for the existence of GDB on the path |
| 89 | + hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; } |
| 90 | + |
| 91 | + echo ----- start =============== GDB Output ===================================================== |
| 92 | + # Open the dump in GDB and print the stack from each thread. We can add more |
| 93 | + # commands here if desired. |
| 94 | + echo printing native stack. |
| 95 | + gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name |
| 96 | + echo ----- end =============== GDB Output ======================================================= |
| 97 | +} |
| 98 | + |
| 99 | +function print_info_from_core_file { |
| 100 | + local core_file_name=$1 |
| 101 | + local executable_name=$RUNTIME_PATH/$2 |
| 102 | + |
| 103 | + if ! [ -e $executable_name ]; then |
| 104 | + echo "Unable to find executable $executable_name" |
| 105 | + return |
| 106 | + elif ! [ -e $core_file_name ]; then |
| 107 | + echo "Unable to find core file $core_file_name" |
| 108 | + return |
| 109 | + fi |
| 110 | + echo "Printing info from core file $core_file_name" |
| 111 | + print_info_from_core_file_using_gdb $core_file_name $executable_name |
| 112 | + print_info_from_core_file_using_lldb $core_file_name $executable_name |
| 113 | +} |
| 114 | + |
| 115 | +function copy_core_file_to_temp_location { |
| 116 | + local core_file_name=$1 |
| 117 | + |
| 118 | + local storage_location="/tmp/coredumps" |
| 119 | + |
| 120 | + # Create the directory (this shouldn't fail even if it already exists). |
| 121 | + mkdir -p $storage_location |
| 122 | + |
| 123 | + local new_location=$storage_location/core.$RANDOM |
| 124 | + |
| 125 | + echo "Copying core file $core_file_name to $new_location in case you need it." |
| 126 | + cp $core_file_name $new_location |
| 127 | +} |
| 128 | + |
| 129 | +# ========================= BEGIN Core File Setup ============================ |
| 130 | +if [ "$(uname -s)" == "Darwin" ]; then |
| 131 | + # On OS X, we will enable core dump generation only if there are no core |
| 132 | + # files already in /cores/ at this point. This is being done to prevent |
| 133 | + # inadvertently flooding the CI machines with dumps. |
| 134 | + if [[ ! -d "/cores" || ! "$(ls -A /cores)" ]]; then |
| 135 | + ulimit -c unlimited |
| 136 | + fi |
| 137 | +elif [ "$(uname -s)" == "Linux" ]; then |
| 138 | + # On Linux, we'll enable core file generation unconditionally, and if a dump |
| 139 | + # is generated, we will print some useful information from it and delete the |
| 140 | + # dump immediately. |
| 141 | + |
| 142 | + if [ -e /proc/self/coredump_filter ]; then |
| 143 | + # Include memory in private and shared file-backed mappings in the dump. |
| 144 | + # This ensures that we can see disassembly from our shared libraries when |
| 145 | + # inspecting the contents of the dump. See 'man core' for details. |
| 146 | + echo -n 0x3F > /proc/self/coredump_filter |
| 147 | + fi |
| 148 | + |
| 149 | + ulimit -c unlimited |
| 150 | +fi |
| 151 | +# ========================= END Core File Setup ============================== |
| 152 | + |
| 153 | +# ========================= BEGIN Test Execution ============================= |
| 154 | +echo ----- start $(date) =============== To repro directly: ===================================================== |
| 155 | +echo pushd $EXECUTION_DIR |
| 156 | +[[RunCommandsEcho]] |
| 157 | +echo popd |
| 158 | +echo =========================================================================================================== |
| 159 | +pushd $EXECUTION_DIR |
| 160 | +[[RunCommands]] |
| 161 | +test_exitcode=$? |
| 162 | +popd |
| 163 | +echo ----- end $(date) ----- exit code $test_exitcode ---------------------------------------------------------- |
| 164 | + |
| 165 | +if [ "${exitcode_list[$test_exitcode]}" != "" ]; then |
| 166 | + echo exit code $test_exitcode means ${exitcode_list[$test_exitcode]} |
| 167 | +fi |
| 168 | +# ========================= END Test Execution =============================== |
| 169 | + |
| 170 | +# ======================= BEGIN Core File Inspection ========================= |
| 171 | +pushd $EXECUTION_DIR >/dev/null |
| 172 | +if [[ "$(uname -s)" == "Linux" && $test_exitcode -ne 0 ]]; then |
| 173 | + echo Looking around for any Linux dump... |
| 174 | + # Depending on distro/configuration, the core files may either be named "core" |
| 175 | + # or "core.<PID>" by default. We read /proc/sys/kernel/core_uses_pid to |
| 176 | + # determine which it is. |
| 177 | + core_name_uses_pid=0 |
| 178 | + if [ -e /proc/sys/kernel/core_uses_pid ] && [ "1" == $(cat /proc/sys/kernel/core_uses_pid) ]; then |
| 179 | + core_name_uses_pid=1 |
| 180 | + fi |
| 181 | + |
| 182 | + if [ $core_name_uses_pid == "1" ]; then |
| 183 | + # We don't know what the PID of the process was, so let's look at all core |
| 184 | + # files whose name matches core.NUMBER |
| 185 | + echo Looking for files matching core.* ... |
| 186 | + for f in core.*; do |
| 187 | + [[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" "dotnet" && copy_core_file_to_temp_location "$f" && rm "$f" |
| 188 | + done |
| 189 | + elif [ -f core ]; then |
| 190 | + echo found a dump named core in $EXECUTION_DIR ! |
| 191 | + print_info_from_core_file "core" "dotnet" |
| 192 | + copy_core_file_to_temp_location "core" |
| 193 | + rm "core" |
| 194 | + else |
| 195 | + echo ... found no dump in $PWD |
| 196 | + fi |
| 197 | +fi |
| 198 | +popd >/dev/null |
| 199 | +# ======================== END Core File Inspection ========================== |
| 200 | +exit $test_exitcode |
0 commit comments