diff --git a/.vsts-pipelines/templates/build-steps.yml b/.vsts-pipelines/templates/build-steps.yml index dbc03620b5..2d6544e107 100644 --- a/.vsts-pipelines/templates/build-steps.yml +++ b/.vsts-pipelines/templates/build-steps.yml @@ -3,16 +3,26 @@ phases: parameters: agentOs: Windows beforeBuild: - - powershell: "& ./tools/UpdateIISExpressCertificate.ps1; & ./tools/update_schema.ps1" + - powershell: "& ./tools/UpdateIISExpressCertificate.ps1; & ./tools/update_schema.ps1; & ./tools/SetupTestEnvironment.ps1 Setup" displayName: Prepare repo afterBuild: + - powershell: "& ./tools/SetupTestEnvironment.ps1 Shutdown" + displayName: Stop AppVerifier + condition: always() - task: PublishBuildArtifacts@1 - displayName: Upload binlog + displayName: Upload logs condition: eq(variables['system.pullrequest.isfork'], false) inputs: artifactName: logs artifactType: Container - pathtoPublish: artifacts/logs/ + pathtoPublish: artifacts/logs + - task: PublishBuildArtifacts@1 + displayName: Upload dumps + condition: eq(variables['system.pullrequest.isfork'], false) + inputs: + artifactName: dumps + artifactType: Container + pathtoPublish: artifacts/dumps - template: .vsts-pipelines/templates/phases/default-build.yml@buildtools parameters: diff --git a/Directory.Build.props b/Directory.Build.props index f015ba2785..a328745f31 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -18,6 +18,8 @@ MicrosoftNuGet true true + + false diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp b/src/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp index ca40d04e36..2a0369206b 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp @@ -75,6 +75,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, break; case DLL_PROCESS_DETACH: g_fProcessDetach = TRUE; + IN_PROCESS_HANDLER::StaticTerminate(); + ALLOC_CACHE_HANDLER::StaticTerminate(); DebugStop(); default: break; diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index b9746ff204..352ec7b093 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -259,7 +259,6 @@ IN_PROCESS_APPLICATION::LoadManagedApplication // Set up stdout redirect SRWExclusiveLock lock(m_stateLock); - if (m_pLoggerProvider == NULL) { hr = LoggingHelpers::CreateLoggingProvider( @@ -394,28 +393,22 @@ IN_PROCESS_APPLICATION::SetEnvironementVariablesOnWorkerProcess( { HRESULT hr = S_OK; ENVIRONMENT_VAR_HASH* pHashTable = NULL; - if (FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( + std::unique_ptr table; + RETURN_IF_FAILED(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( m_pConfig->QueryEnvironmentVariables(), m_pConfig->QueryWindowsAuthEnabled(), m_pConfig->QueryBasicAuthEnabled(), m_pConfig->QueryAnonymousAuthEnabled(), - &pHashTable))) - { - goto Finished; - } + &pHashTable)); - pHashTable->Apply(ENVIRONMENT_VAR_HELPERS::AppendEnvironmentVariables, &hr); - if (FAILED(hr)) - { - goto Finished; - } - pHashTable->Apply(ENVIRONMENT_VAR_HELPERS::SetEnvironmentVariables, &hr); - if (FAILED(hr)) - { - goto Finished; - } -Finished: - return hr; + table.reset(pHashTable); + + table->Apply(ENVIRONMENT_VAR_HELPERS::AppendEnvironmentVariables, &hr); + RETURN_IF_FAILED(hr); + + table->Apply(ENVIRONMENT_VAR_HELPERS::SetEnvironmentVariables, &hr); + RETURN_IF_FAILED(hr); + return S_OK; } HRESULT diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.cpp b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.cpp index 90b3bd9d53..0b7357f5d2 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.cpp +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.cpp @@ -180,11 +180,18 @@ HRESULT Finished: if (FAILED(hr)) { - if (sm_pAlloc != NULL) - { - delete sm_pAlloc; - sm_pAlloc = NULL; - } + StaticTerminate(); } return hr; } + +// static +void +IN_PROCESS_HANDLER::StaticTerminate(VOID) +{ + if (sm_pAlloc != NULL) + { + delete sm_pAlloc; + sm_pAlloc = NULL; + } +} diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.h b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.h index fe0331da9b..ac6aa24789 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.h +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocesshandler.h @@ -70,6 +70,10 @@ public: HRESULT StaticInitialize(VOID); + static + void + StaticTerminate(VOID); + private: REQUEST_NOTIFICATION_STATUS ServerShutdownMessage() const; diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp index d74571e953..d77c5c47d8 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp @@ -260,6 +260,8 @@ BOOL APIENTRY DllMain(HMODULE hModule, break; case DLL_PROCESS_DETACH: g_fProcessDetach = TRUE; + FORWARDING_HANDLER::StaticTerminate(); + ALLOC_CACHE_HANDLER::StaticTerminate(); DebugStop(); default: break; diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h b/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h index 8fa054f2a9..98bcd5ada4 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h +++ b/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h @@ -76,6 +76,7 @@ private: mutable LONG _cRefs; }; + class ENVIRONMENT_VAR_HASH : public HASH_TABLE { public: @@ -129,3 +130,12 @@ private: ENVIRONMENT_VAR_HASH(const ENVIRONMENT_VAR_HASH &); void operator=(const ENVIRONMENT_VAR_HASH &); }; + +struct ENVIRONMENT_VAR_HASH_DELETER +{ + void operator ()(ENVIRONMENT_VAR_HASH* hashTable) const + { + hashTable->Clear(); + delete hashTable; + } +}; diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs index d2df4a8f8c..8c07bec312 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs @@ -341,7 +341,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS throw new InvalidOperationException("Site not stopped yet"); } - if (appPool.WorkerProcesses.Any(wp => wp.State == WorkerProcessState.Running || + if (appPool.WorkerProcesses != null && appPool.WorkerProcesses.Any(wp => wp.State == WorkerProcessState.Running || wp.State == WorkerProcessState.Stopping)) { throw new InvalidOperationException("WorkerProcess not stopped yet"); diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs index 5e6aabdadb..be6f39edec 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployerBase.cs @@ -100,7 +100,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS private void AddWebConfigEnvironmentVariables(XElement element, string contentRoot) { var environmentVariables = element - .RequiredElement("system.webServer") + .Descendants("system.webServer") + .Single() .RequiredElement("aspNetCore") .GetOrAdd("environmentVariables"); @@ -114,7 +115,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS private void AddHandlerSettings(XElement element, string contentRoot) { var handlerSettings = element - .RequiredElement("system.webServer") + .Descendants("system.webServer") + .Single() .RequiredElement("aspNetCore") .GetOrAdd("handlerSettings"); diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs index 589907657f..f920622568 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/WebConfigHelpers.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using System.Xml.Linq; namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS @@ -12,7 +13,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS { return (element, _) => { element - .GetOrAdd("system.webServer") + .Descendants("system.webServer") + .Single() .GetOrAdd("aspNetCore") .SetAttributeValue(key, value); }; @@ -23,7 +25,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS return (element, _) => { element - .GetOrAdd("system.webServer") + .Descendants("system.webServer") + .Single() .GetOrAdd("handlers") .GetOrAdd("add") .SetAttributeValue(key, value); diff --git a/test/Common.FunctionalTests/MultiApplicationTests.cs b/test/Common.FunctionalTests/MultiApplicationTests.cs index a91dfa4d76..009c61f373 100644 --- a/test/Common.FunctionalTests/MultiApplicationTests.cs +++ b/test/Common.FunctionalTests/MultiApplicationTests.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; @@ -78,7 +79,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests var webConfigLocation = GetWebConfigLocation(directory); XDocument webConfig = XDocument.Load(webConfigLocation); webConfig.Root - .GetOrAdd("system.webServer") + .Descendants("system.webServer") + .Single() .GetOrAdd("aspNetCore") .SetAttributeValue("hostingModel", model.ToString()); webConfig.Save(webConfigLocation); diff --git a/tools/SetupTestEnvironment.ps1 b/tools/SetupTestEnvironment.ps1 new file mode 100644 index 0000000000..6fabf1704b --- /dev/null +++ b/tools/SetupTestEnvironment.ps1 @@ -0,0 +1,96 @@ +param($Mode) + +function Setup-appverif($application) +{ + appverif.exe -enable Exceptions Handles Heaps Leak Locks Memory Threadpool TLS SRWLock -for $application + $onlyLog = 0x181; + $codes = @( + # Exceptions + 0x650, + # Handles + 0x300, 0x301, 0x302, 0x303, 0x304, 0x305, + # Heaps + 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F, 0x010, 0x011, 0x012, 0x013, 0x014, + # Leak + 0x900, 0x901, 0x902, 0x903, 0x904, 0x905, 0x906, + # Locks + 0x200, 0x201, 0x202, 0x203, 0x204, 0x205, 0x206, 0x207, 0x208, 0x209, 0x210, 0x211, 0x212, 0x213, 0x214, 0x215, + # Memory + 0x600, 0x601, 0x602, 0x603, 0x604, 0x605, 0x606, 0x607, 0x608, 0x609, 0x60A, 0x60B, 0x60C, 0x60D, 0x60E, 0x60F, 0x610, 0x612, 0x613, 0x614, 0x615, 0x616, 0x617, 0x618, 0x619, 0x61A, 0x61B, 0x61C, 0x61D, 0x61E, + # SRWLock + 0x250, 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257, + # TSL + 0x350, 0x351, 0x352, + # ThreadPool + 0x700, 0x701, 0x702, 0x703, 0x704, 0x705, 0x706, 0x707, 0x708, 0x709, 0x70A, 0x70B, 0x70C, 0x70D + ); + appverif.exe -configure $codes -for $application -with ErrorReport=$onlyLog +} + +function Shutdown-appverif($application) +{ + appverif.exe -export log -for $application -with To=$LogsFolder\$application.xml Log=0 + appverif.exe -disable * -for $application +} + +$DumpFolder = "$PSScriptRoot\..\artifacts\dumps" +if (!(Test-Path $DumpFolder)) +{ + New-Item $DumpFolder -ItemType Directory; +} +$DumpFolder = Resolve-Path $DumpFolder + +$LogsFolder = "$PSScriptRoot\..\artifacts\logs" +if (!(Test-Path $LogsFolder)) +{ + New-Item $LogsFolder -ItemType Directory; +} +$LogsFolder = Resolve-Path $LogsFolder + +$werHive = "HKLM:\SOFTWARE\Microsoft\Windows\Windows Error Reporting"; +$ldHive = "$werHive\LocalDumps"; + + +$cdb = "c:\Program Files (x86)\Windows Kits\10\Debuggers\x64\cdb.exe" +if (!(Test-Path $cdb)) +{ + $downloadedFile = [System.IO.Path]::GetTempFileName(); + $downloadedFile = "$downloadedFile.exe"; + Invoke-WebRequest -Uri "https://go.microsoft.com/fwlink/p/?linkid=870807" -OutFile $downloadedFile; + & $downloadedFile /features OptionId.WindowsDesktopDebuggers /norestart /q; +} + +if ($Mode -eq "Setup") +{ + #Setup-appverif w3wp.exe + #Setup-appverif iisexpress.exe + + if (!(Test-Path $ldHive )) + { + New-Item -Path $werHive -Name LocalDumps + } + + New-ItemProperty $ldHive -Name "DumpFolder" -Value $DumpFolder -PropertyType "ExpandString" -Force; + New-ItemProperty $ldHive -Name "DumpCount" -Value 15 -PropertyType "DWORD" -Force; + New-ItemProperty $ldHive -Name "DumpType" -Value 2 -PropertyType "DWORD" -Force; + + Restart-Service WerSvc +} + +if ($Mode -eq "Shutdown") +{ + Remove-Item $ldHive -Recurse -Force + + #Shutdown-appverif w3wp.exe + #Shutdown-appverif iisexpress.exe + + foreach ($dump in (Get-ChildItem -Path $DumpFolder -Filter "*.dmp")) + { + if (Test-Path $cdb) + { + & $cdb -z $dump.FullName -y "https://msdl.microsoft.com/download/symbols" -c ".loadby sos coreclr;!sym noisy;.reload /f;.dumpcab -a $($dump.FullName).cab;q;" + } + } +} + +Exit 0; \ No newline at end of file diff --git a/tools/stresstest.ps1 b/tools/stresstest.ps1 deleted file mode 100644 index 981c6fcf44..0000000000 --- a/tools/stresstest.ps1 +++ /dev/null @@ -1,96 +0,0 @@ -########################################################## -# NOTE: -# For running test automation, following prerequisite required: -# -# 1. On Win7, powershell should be upgraded to 4.0 -# https://social.technet.microsoft.com/wiki/contents/articles/21016.how-to-install-windows-powershell-4-0.aspx -# 2. url-rewrite should be installed -# 3. makecert.exe tools should be available -########################################################## - -# Replace aspnetcore.dll with the latest version -copy C:\gitroot\AspNetCoreModule\artifacts\build\AspNetCore\bin\Release\x64\aspnetcore.dll "C:\Program Files\IIS Express" -copy C:\gitroot\AspNetCoreModule\artifacts\build\AspNetCore\bin\Release\x64\aspnetcore.pdb "C:\Program Files\IIS Express" - - -# Enable appverif for IISExpress.exe -appverif /verify iisexpress.exe - -# Set the AspNetCoreModuleTest environment variable with the following command -cd C:\gitroot\AspNetCoreModule\test\AspNetCoreModule.Test -dotnet restore -dotnet build -$aspNetCoreModuleTest="C:\gitroot\AspNetCoreModule\test\AspNetCoreModule.Test\bin\Debug\net46" - -if (Test-Path (Join-Path $aspNetCoreModuleTest aspnetcoremodule.test.dll)) -{ - # Clean up applicationhost.config of IISExpress - del $env:userprofile\documents\iisexpress\config\applicationhost.config -Confirm:$false -Force - Start-Process "C:\Program Files\IIS Express\iisexpress.exe" - Sleep 3 - Stop-Process -Name iisexpress - - # Create sites - (1..50) | foreach { md ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) 2> out-null } - (1..50) | foreach { copy C:\gitroot\AspNetCoreModule\test\StressTestWebRoot\web.config ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) } - (1..50) | foreach { - $path = ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) - $appPath = "/foo"+$_ - & "C:\Program Files\IIS Express\appcmd.exe" add app /site.name:"WebSite1" /path:$appPath /physicalPath:$path - } - - <#(1..50) | foreach { - $configpath = ("WebSite1/foo" + $_) - $value = "C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ + ".exe" - & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /processPath:$value - } - (1..50) | foreach { copy C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo.exe ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ +".exe") } - (1..50) | foreach { - $configpath = ("WebSite1/foo" + $_) - $value = "%AspNetCoreModuleTest%\AspnetCoreApp_HelloWeb\foo" + $_ + ".exe" - & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /processPath:$value /apphostconfig:%AspNetCoreModuleTest%\config\applicationhost.config - - $value = "%AspNetCoreModuleTest%\AspnetCoreApp_HelloWeb\AutobahnTestServer.dll" - & "C:\Program Files\IIS Express\appcmd.exe" set config $configpath -section:system.webServer/aspNetCore /arguments:$value /apphostconfig:%AspNetCoreModuleTest%\config\applicationhost.config - } - #> - - # Start IISExpress with running the below command - &"C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe" /g /G "C:\Program Files\IIS Express\iisexpress.exe" - - - # 6. Start stress testing - (1..10000) | foreach { - if ($_ % 2 -eq 0) - { - ("Recycling backend only") - stop-process -name dotnet - (1..50) | foreach { del ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ + "\app_offline.htm") -confirm:$false -Force 2> out-null } - stop-process -name dotnet - } - else - { - ("Recycling backedn + enabling appoffline ....") - stop-process -name dotnet - (1..50) | foreach { copy C:\gitroot\AspNetCoreModule\test\StressTestWebRoot\app_offline.htm ("C:\inetpub\wwwroot\AspnetCoreHandler_HelloWeb\foo" + $_ ) } - } - Sleep 1 - - (1..10) | foreach { - (1..50) | foreach { - invoke-webrequest ("http://localhost:8080/foo"+$_) > $null - } - } - } - - - # Stress test idea - # 1. Use Web Stress Tester - # 2. Run stop-process -name dotnet - # 3. Hit Q command to IISExpress console window - # 4. Use app_offline.htm - # 5. Save dummy web.config -} - -// bp aspnetcore!FORWARDING_HANDLER::FORWARDING_HANDLER -// bp aspnetcore!FORWARDING_HANDLER::~FORWARDING_HANDLER \ No newline at end of file