Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
2d1eafc
[DO NOT MERGE | REVERT] - Forced AV in brotli native code. Disable un…
carlossanlop Nov 7, 2023
5242ff3
gen-debug-dump-docs.py: More meaningful messages
carlossanlop Nov 7, 2023
070fd63
XUnitLogChecker.cs: Minor fixes
carlossanlop Nov 7, 2023
c178f37
CoreclrTestWrapperLib.cs: Avoid passing null coreRoot string to Path.…
carlossanlop Nov 7, 2023
ce72a2a
RunnerTemplate.cmd: Invoke XUnitLogChecker
carlossanlop Nov 7, 2023
a57ced6
RunnerTemplate.sh: Invoke XUnitLogChecker
carlossanlop Nov 7, 2023
6d04923
sendtohelix.proj: Set __TestArchitecture and build XUnitLogChecker
carlossanlop Nov 7, 2023
173911f
Fix cmd with a goto because delayed execution prevents setting variab…
carlossanlop Nov 7, 2023
9414c81
Minor sh improvements and verbosity additions
carlossanlop Nov 7, 2023
e4c94ac
Move to tests.proj
carlossanlop Nov 7, 2023
02db8bc
Move IsTargetOSSupportedByXUnitLogChecker to root Directory.Build.props.
carlossanlop Nov 8, 2023
fb616c5
Simplify project reference in tests.proj
carlossanlop Nov 8, 2023
6e077e6
Remove coredump_filter and ulimit configuration, since helix machines…
carlossanlop Nov 8, 2023
044cb20
Rename IsTargetOSSupportedByXUnitLogChecker to IsXUnitLogCheckerSuppo…
carlossanlop Nov 9, 2023
2732b61
Skip XUnitLogChecker execution in Net48 libraries test runs.
carlossanlop Nov 9, 2023
e07e28b
Re-add the accidentally removed HelixCorrelationPayload, but with the…
carlossanlop Nov 9, 2023
52df19c
Build XUnitLogChecker in tests.proj
carlossanlop Nov 9, 2023
c04a72a
Add an empty Tests target and a copy of ZipTestArchive to the XUnitLo…
carlossanlop Nov 9, 2023
b047c8c
Condition the custom ZipTestArchive inside XUnitLogChecker.csproj
carlossanlop Nov 9, 2023
8189449
Separate framework check from IsXUnitLogCheckerSupported, the value i…
carlossanlop Nov 9, 2023
3bd400f
Change cmd env var to int
carlossanlop Nov 9, 2023
0b5bd2a
typo typo typo
carlossanlop Nov 9, 2023
7123d0f
Separate the directory paths into two, they have different purposes.
carlossanlop Nov 9, 2023
384fdf5
Fix helix path
carlossanlop Nov 9, 2023
7c3d0fe
The zip goes under artifacts, not artifacts/bin
carlossanlop Nov 10, 2023
26db023
Skip in NativeAot tests
carlossanlop Nov 10, 2023
93e33c2
Always set the value of _IsXUnitLogCheckerSupported in the cmd and sh…
carlossanlop Nov 10, 2023
d3fe8fa
Add verbose messages for IsXUnitLogCeckerSupported possible values, a…
carlossanlop Nov 10, 2023
01541fc
Try disabling using TestNativeAot instead
carlossanlop Nov 10, 2023
cda54c1
Do not attempt to run libs.tests on XUnitLogChecker
carlossanlop Nov 10, 2023
ec0774b
Remove temp messages
carlossanlop Nov 10, 2023
2c9ec70
Better name for internal property in sendtohelix.proj
carlossanlop Nov 10, 2023
11fa84a
Temp var that prints literal value of IsXUnitLogCheckerSupported
carlossanlop Nov 10, 2023
c955c5e
Move the MSBuildProjectName condition to the RunTests target itself
carlossanlop Nov 10, 2023
dfa7985
Remove the test target from XUnitLogChecker.csproj
carlossanlop Nov 10, 2023
299a454
Set OutDir value directly
carlossanlop Nov 10, 2023
942687b
TEMP: Print value of property in send to helix
carlossanlop Nov 10, 2023
4c9a8ff
Put output directory somewhere else so we dont assume XUnitLogChecker…
carlossanlop Nov 10, 2023
6f609e9
Remove the undefined env var check in the cmd.
carlossanlop Nov 10, 2023
bf96801
Use BuildTargetFramework to set the env var in sendtohelixhelp.proj
carlossanlop Nov 10, 2023
ca3522e
Use NetCoreAppCurrent
carlossanlop Nov 10, 2023
0345424
Fix temp conflict with base
carlossanlop Nov 10, 2023
3ca551a
The Libraries Build leg needs to also publish the artifacts/bin/XUnit…
carlossanlop Nov 10, 2023
c417c25
Extra spacing
carlossanlop Nov 10, 2023
5c46f37
Only set envvars inside the condition
carlossanlop Nov 10, 2023
c2243dc
The cmd condition does not work unless double quoted
carlossanlop Nov 10, 2023
d94e151
TMP: Printed final value
carlossanlop Nov 10, 2023
75a4033
Add fi accidentally removed
carlossanlop Nov 10, 2023
d6da591
Move the Linux-only code to its own if, and everything else should ru…
carlossanlop Nov 10, 2023
6568293
Ensure the OSX core folder is also visited.
carlossanlop Nov 11, 2023
85b5e0b
Revert temporary changes
carlossanlop Nov 11, 2023
25f6a70
[DO NOT MERGE | REVERT] Cause a crash and disable unrelated CI legs
carlossanlop Nov 13, 2023
f7d7e50
Simplify envvar condition in sendtohelixhelp.proj
carlossanlop Nov 14, 2023
b926291
Move build target framework check up to the property group.
carlossanlop Nov 14, 2023
69df4b6
Missed a sh variable $
carlossanlop Nov 14, 2023
bb772dd
Remove should set variable from outside the condition, it's unused
carlossanlop Nov 14, 2023
3686646
In OSX, $HELIX_DUMP_FOLDER is passed as `/cores`, so we don't need to…
carlossanlop Nov 14, 2023
87f7055
Missed a ;
carlossanlop Nov 14, 2023
3da51fb
Another missed ;
carlossanlop Nov 14, 2023
0d15bf8
Use the $core_file_name when renaming the dmp
carlossanlop Nov 14, 2023
bcd2acf
Remove sleep
carlossanlop Nov 14, 2023
7857f36
Remove $RANDOM
carlossanlop Nov 15, 2023
fab3449
Revert the temporary crash and the disabled CI legs.
carlossanlop Nov 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@
<TestExclusionListTasksAssemblyPath>$([MSBuild]::NormalizePath('$(TestExclusionListTasksDir)', 'TestExclusionListTasks.dll'))</TestExclusionListTasksAssemblyPath>
<CoreCLRToolPath>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'coreclr', '$(TargetOS).$(TargetArchitecture).$(RuntimeConfiguration)'))</CoreCLRToolPath>
<ILAsmToolPath Condition="'$(DotNetBuildFromSource)' == 'true' or '$(BuildArchitecture)' == 's390x' or '$(BuildArchitecture)' == 'ppc64le'">$(CoreCLRToolPath)</ILAsmToolPath>

<WasmtimeDir Condition="'$(WasmtimeDir)' == '' and '$(WASMTIME_PATH)' != '' and Exists($(WASMTIME_PATH))">$(WASMTIME_PATH)</WasmtimeDir>
<WasmtimeDir Condition="'$(WasmtimeDir)' == ''">$([MSBuild]::NormalizeDirectory($(ArtifactsObjDir), 'wasmtime'))</WasmtimeDir>
<InstallWasmtimeForTests Condition="'$(InstallWasmtimeForTests)' == '' and !Exists($(WasmtimeDir))">true</InstallWasmtimeForTests>
Expand Down Expand Up @@ -332,6 +331,11 @@

<!-- this property is used by the SDK to pull in mono-based runtime packs -->
<UseMonoRuntime Condition="'$(UseMonoRuntime)' == '' and '$(RuntimeFlavor)' == 'Mono'">true</UseMonoRuntime>

<!-- For enabling the use of XUnitLogChecker in coreclr and libraries test runs. -->
<IsXUnitLogCheckerSupported Condition="'$(IsXUnitLogCheckerSupported)' == ''">false</IsXUnitLogCheckerSupported>
<IsXUnitLogCheckerSupported Condition="'$(RuntimeFlavor)' == 'CoreCLR' and '$(TestNativeAot)' != 'true' and '$(TestRunNamePrefixSuffix)' != 'NativeAOT_Release' and '$(TargetOS)' != 'browser' and '$(TargetOS)' != 'wasi' and '$(TargetOS)' != 'ios' and '$(TargetOS)' != 'iossimulator' and '$(TargetOS)' != 'tvos' and '$(TargetOS)' != 'tvossimulator' and '$(TargetOS)' != 'maccatalyst' and '$(TargetOS)' != 'android'">true</IsXUnitLogCheckerSupported>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to condition on TestRunNamePrefixSuffix? NativeAOT is already excluded via the TestNativeAot property, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When it was only TestNativeAot, I was still seeing failures last night in some NativeAOT cases. I realized TestRunNamePrefixSuffix was another value that was used when running tests that could help detect aot tests, and now it worked.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you need both in that case? Also, runtime flavor should remove a lot of the TargetOS ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed some NativeAot runs were not getting ignored unless I added both conditions. They are temporary anyway. I opened an issue so the NativeAOT owners can get it enabled properly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having to condition on '$(TestRunNamePrefixSuffix)' != 'NativeAOT_Release' is extremely suspicious. We only ever run native AOT testing with TestNativeAot. It's not native AOT testing if that property is not true. This will likely produce obscure failure modes if we add new test pipelines or rename existing. It will be expensive to root cause for whoever isn't already aware of this hack. Cc @dotnet/ilc-contrib for awareness.

They are temporary anyway. I opened an issue so the NativeAOT owners can get it enabled properly.

Unless you already talked to some "NativeAOT owners" and scheduled their time to fix this, this temporary workaround is likely to be in the product for a long time, like most temporary workarounds. I'm very much against this condition being checked in as it is, please investigate this. One thing off top of my head is that this might need to check for RunNativeAotTestApps instead, which gates special kinds of library tests that was added in #76109.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MichalStrehovsky Since I had to revert the change from main due to NativeAOT tests being broken in outerloop, I can remove the TestRunNamePrefixSuffix property from the condition in the new PR.
I also opened #94722 to track enabling them in the future.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! The issue might have some overlap with #94270.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to mark mine as duplicate of yours if they're tracking the same.

<XUnitLogCheckerLibrariesOutDir>$([MSBuild]::NormalizeDirectory('$(ArtifactsBinDir)', 'XUnitLogChecker'))</XUnitLogCheckerLibrariesOutDir>
</PropertyGroup>

<!-- Packaging -->
Expand Down
1 change: 1 addition & 0 deletions eng/pipelines/libraries/prepare-for-bin-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ steps:
ref/**
runtime/**
testhost/**
XUnitLogChecker/**

- task: CopyFiles@2
displayName: Prepare bin folders to publish
Expand Down
35 changes: 35 additions & 0 deletions eng/testing/RunnerTemplate.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,41 @@ if %_exit_code%==1 (
)
)
)

if NOT "%__IsXUnitLogCheckerSupported%"=="1" (
echo XUnitLogChecker not supported for this test case. Skipping.
GOTO SKIP_XUNITLOGCHECKER
)

echo ----- start =============== XUnitLogChecker Output =====================================================

set DOTNET_EXE=%RUNTIME_PATH%\dotnet.exe
set XUNITLOGCHECKER_DLL=%HELIX_CORRELATION_PAYLOAD%\XUnitLogChecker.dll
set XUNITLOGCHECKER_COMMAND=%DOTNET_EXE% --roll-forward Major %XUNITLOGCHECKER_DLL% --dumps-path %HELIX_DUMP_FOLDER%
set XUNITLOGCHECKER_EXIT_CODE=1

if NOT EXIST %DOTNET_EXE% (
echo dotnet.exe does not exist in the expected location: %DOTNET_EXE%
GOTO XUNITLOGCHECKER_END
) else if NOT EXIST %XUNITLOGCHECKER_DLL% (
echo XUnitLogChecker.dll does not exist in the expected location: %XUNITLOGCHECKER_DLL%
GOTO XUNITLOGCHECKER_END
)

echo %XUNITLOGCHECKER_COMMAND%
%XUNITLOGCHECKER_COMMAND%
set XUNITLOGCHECKER_EXIT_CODE=%ERRORLEVEL%

:XUNITLOGCHECKER_END

if %XUNITLOGCHECKER_EXIT_CODE% NEQ 0 (
set _exit_code=%XUNITLOGCHECKER_EXIT_CODE%
)

echo ----- end =============== XUnitLogChecker Output - exit code %XUNITLOGCHECKER_EXIT_CODE% ===============

:SKIP_XUNITLOGCHECKER

exit /b %_exit_code%
:: ========================= END Test Execution =================================

Expand Down
178 changes: 78 additions & 100 deletions eng/testing/RunnerTemplate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,80 +54,60 @@ exitcode_list[131]="SIGQUIT Ctrl-\ occurred. Core dumped."
exitcode_list[132]="SIGILL Illegal Instruction. Core dumped. Likely codegen issue."
exitcode_list[133]="SIGTRAP Breakpoint hit. Core dumped."
exitcode_list[134]="SIGABRT Abort. Managed or native assert, or runtime check such as heap corruption, caused call to abort(). Core dumped."
exitcode_list[135]="IGBUS Unaligned memory access. Core dumped."
exitcode_list[135]="IGBUS Unaligned memory access. Core dumped."
exitcode_list[136]="SIGFPE Bad floating point arguments. Core dumped."
exitcode_list[137]="SIGKILL Killed eg by kill"
exitcode_list[139]="SIGSEGV Illegal memory access. Deref invalid pointer, overrunning buffer, stack overflow etc. Core dumped."
exitcode_list[143]="SIGTERM Terminated. Usually before SIGKILL."
exitcode_list[159]="SIGSYS Bad System Call."

function print_info_from_core_file_using_lldb {
function move_core_file_to_temp_location {
local core_file_name=$1
local executable_name=$2
local plugin_path_name="$RUNTIME_PATH/shared/Microsoft.NETCore.App/9.9.9/libsosplugin.so"

# check for existence of lldb on the path
hash lldb 2>/dev/null || { echo >&2 "lldb was not found. Unable to print core file."; return; }
# Append the dmp extension to ensure XUnitLogChecker finds it
local new_location=$HELIX_DUMP_FOLDER/$core_file_name.dmp

# pe, clrstack, and dumpasync are defined in libsosplugin.so
if [ ! -f $plugin_path_name ]; then
echo $plugin_path_name cannot be found.
return
fi

echo ----- start =============== lldb Output =====================================================
echo Printing managed exceptions, managed call stacks, and async state machines.
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
echo ----- end =============== lldb Output =======================================================
}

function print_info_from_core_file_using_gdb {
local core_file_name=$1
local executable_name=$2

# Check for the existence of GDB on the path
hash gdb 2>/dev/null || { echo >&2 "GDB was not found. Unable to print core file."; return; }
echo "Copying dump file '$core_file_name' to '$new_location'"
cp $core_file_name $new_location

echo ----- start =============== GDB Output =====================================================
# Open the dump in GDB and print the stack from each thread. We can add more
# commands here if desired.
echo printing native stack.
gdb --batch -ex "thread apply all bt full" -ex "quit" $executable_name $core_file_name
echo ----- end =============== GDB Output =======================================================
# Delete the old one
rm $core_file_name
}

function print_info_from_core_file {
local core_file_name=$1
local executable_name=$RUNTIME_PATH/$2

if ! [ -e $executable_name ]; then
echo "Unable to find executable $executable_name"
return
elif ! [ -e $core_file_name ]; then
echo "Unable to find core file $core_file_name"
return
xunitlogchecker_exit_code=0
function invoke_xunitlogchecker {
local dump_folder=$1

total_dumps=$(find $dump_folder -name "*.dmp" | wc -l)

if [[ $total_dumps > 0 ]]; then
echo "Total dumps found in $dump_folder: $total_dumps"
xunitlogchecker_file_name="$HELIX_CORRELATION_PAYLOAD/XUnitLogChecker.dll"
dotnet_file_name="$RUNTIME_PATH/dotnet"

if [[ ! -f $dotnet_file_name ]]; then
echo "'$dotnet_file_name' was not found. Unable to run XUnitLogChecker."
xunitlogchecker_exit_code=1
elif [[ ! -f $xunitlogchecker_file_name ]]; then
echo "'$xunitlogchecker_file_name' was not found. Unable to print dump file contents."
xunitlogchecker_exit_code=2
elif [[ ! -d $dump_folder ]]; then
echo "The dump directory '$dump_folder' does not exist."
else
echo "Executing XUnitLogChecker in $dump_folder..."
cmd="$dotnet_file_name --roll-forward Major $xunitlogchecker_file_name --dumps-path $dump_folder"
echo "$cmd"
$cmd
xunitlogchecker_exit_code=$?
fi
else
echo "No dumps found in $dump_folder."
fi
echo "Printing info from core file $core_file_name"
print_info_from_core_file_using_gdb $core_file_name $executable_name
print_info_from_core_file_using_lldb $core_file_name $executable_name
}

function copy_core_file_to_temp_location {
local core_file_name=$1

local storage_location="/tmp/coredumps"

# Create the directory (this shouldn't fail even if it already exists).
mkdir -p $storage_location

local new_location=$storage_location/core.$RANDOM

echo "Copying core file $core_file_name to $new_location in case you need it."
cp $core_file_name $new_location
}

# ========================= BEGIN Core File Setup ============================
if [[ "$(uname -s)" == "Darwin" ]]; then
system_name="$(uname -s)"
if [[ $system_name == "Darwin" ]]; then
# On OS X, we will enable core dump generation only if there are no core
# files already in /cores/ at this point. This is being done to prevent
# inadvertently flooding the CI machines with dumps.
Expand All @@ -140,21 +120,11 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
# https://github.com/dotnet/core-eng/issues/15597
ulimit -c 0
fi

elif [[ "$(uname -s)" == "Linux" ]]; then
# On Linux, we'll enable core file generation unconditionally, and if a dump
# is generated, we will print some useful information from it and delete the
# dump immediately.

if [ -e /proc/self/coredump_filter ]; then
# Include memory in private and shared file-backed mappings in the dump.
# This ensures that we can see disassembly from our shared libraries when
# inspecting the contents of the dump. See 'man core' for details.
echo -n 0x3F > /proc/self/coredump_filter
fi

ulimit -c unlimited
fi

export DOTNET_DbgEnableMiniDump=1
export DOTNET_EnableCrashReport=1
export DOTNET_DbgMiniDumpName=$HELIX_DUMP_FOLDER/coredump.%d.dmp
# ========================= END Core File Setup ==============================

# ========================= BEGIN support for SuperPMI collection ==============================
Expand All @@ -171,7 +141,7 @@ if [ ! -z $spmi_enable_collection ]; then
fi
mkdir -p $spmi_collect_dir
export spmi_file_extension=so
if [[ "$(uname -s)" == "Darwin" ]]; then
if [[ $system_name == "Darwin" ]]; then
export spmi_file_extension=dylib
fi
export SuperPMIShimLogPath=$spmi_collect_dir
Expand Down Expand Up @@ -220,51 +190,59 @@ if [[ $test_exitcode -ne 0 ]]; then
echo ulimit -c value: $(ulimit -c)
fi

if [[ "$(uname -s)" == "Linux" && $test_exitcode -ne 0 ]]; then
if [ -n "$HELIX_WORKITEM_PAYLOAD" ]; then

# For abrupt failures, in Helix, dump some of the kernel log, in case there is a hint
if [[ $test_exitcode -ne 1 ]]; then
dmesg | tail -50
fi

have_sleep=$(which sleep)
if [ -x "$have_sleep" ]; then
echo Waiting a few seconds for any dump to be written..
sleep 10s
fi
fi

if [[ $system_name == "Linux" && $test_exitcode -ne 0 ]]; then
echo cat /proc/sys/kernel/core_pattern: $(cat /proc/sys/kernel/core_pattern)
echo cat /proc/sys/kernel/core_uses_pid: $(cat /proc/sys/kernel/core_uses_pid)
echo cat /proc/sys/kernel/coredump_filter: $(cat /proc/sys/kernel/coredump_filter)

echo Looking around for any Linux dump..

# Depending on distro/configuration, the core files may either be named "core"
# or "core.<PID>" by default. We read /proc/sys/kernel/core_uses_pid to
# determine which it is.
core_name_uses_pid=0
if [[ -e /proc/sys/kernel/core_uses_pid && "1" == $(cat /proc/sys/kernel/core_uses_pid) ]]; then
core_name_uses_pid=1
fi

# The osx dumps are too large to egress the machine
echo Looking around for any Linux dumps...

if [[ "$core_name_uses_pid" == "1" ]]; then
# We don't know what the PID of the process was, so let's look at all core
# files whose name matches core.NUMBER
echo Looking for files matching core.* ...
for f in core.*; do
[[ $f =~ core.[0-9]+ ]] && print_info_from_core_file "$f" "dotnet" && copy_core_file_to_temp_location "$f" && rm "$f"
echo "Looking for files matching core.* ..."
for f in $(find . -name "core.*"); do
[[ $f =~ core.[0-9]+ ]] && move_core_file_to_temp_location "$f"
done
elif [ -f core ]; then
echo found a dump named core in $EXECUTION_DIR !
print_info_from_core_file "core" "dotnet"
copy_core_file_to_temp_location "core"
rm "core"
else
echo ... found no dump in $PWD
fi

if [ -f core ]; then
move_core_file_to_temp_location "core"
fi
fi

if [ -n "$HELIX_WORKITEM_PAYLOAD" ]; then
# For abrupt failures, in Helix, dump some of the kernel log, in case there is a hint
if [[ $test_exitcode -ne 1 ]]; then
dmesg | tail -50
fi

fi

if [[ -z "$__IsXUnitLogCheckerSupported" ]]; then
echo "The '__IsXUnitLogCheckerSupported' env var is not set."
elif [[ "$__IsXUnitLogCheckerSupported" != "1" ]]; then
echo "XUnitLogChecker not supported for this test case. Skipping."
else
echo ----- start =============== XUnitLogChecker Output =====================================================

invoke_xunitlogchecker "$HELIX_DUMP_FOLDER"

if [[ $xunitlogchecker_exit_code -ne 0 ]]; then
test_exitcode=$xunitlogchecker_exit_code
fi
echo ----- end =============== XUnitLogChecker Output - exit code $xunitlogchecker_exit_code ===========================
fi

popd >/dev/null
# ======================== END Core File Inspection ==========================
# The helix work item should not exit with non-zero if tests ran and produced results
Expand Down
Loading