Использование Powershell для автоматизации VSPerfCmd.exe (профилирование производительности Visual Studio)
Поскольку этот вопрос является попыткой устранить большую проблему с моей стороны, я включил в свой пост существенную историю и историю расследований. Таким образом, я разбил пост на 3 раздела: "Предыстория", "Расследование пока" и "Проблема". Поскольку я провел дополнительное расследование самостоятельно, я также добавил раздел "Дополнительное расследование", чтобы расширить свои выводы. В конечном итоге это привело меня к решению проблемы самостоятельно, и я включил оригинальную статью в качестве руководства для любого разработчика, пытающегося сделать то же самое. В конечном итоге эта проблема закончилась возвращением контроля, который VSPerfCmd.exe имел в версиях Powershell после v1.
- Фон
Как часть моей сборки TFS (которая включает в себя автоматическое развертывание на моем веб-сервере разработки DEV), я хотел бы запустить и запустить тест производительности моего кода, чтобы я мог видеть, когда новые изменения негативно влияют на скорость моего API. С этой целью я установил на DEV тестовый прогон (SoapUI) и VS Team Tools и написал скрипт Powershell, который при локальном запуске на DEV создает нужные мне отчеты. Однако мне еще не удалось запустить этот скрипт и заставить его работать из любого другого места. Под этим я подразумеваю, что, только войдя на сервер, найдя файл.ps1 и запустив его в Powershell, он работает. Вот этот скрипт:
#script location+name \\DEV\C$\PerformanceTest\profile-tracing-SoapUI.ps1
$startPath = Get-Location
$siteUrl = "http://api.dev.com"
$sleepTime = 5
$logLocation = "C:\reports\api"
$websiteLocation = "W:\Sites\API\Code\API\_PublishedWebsites\API\bin"
try
{
#Instrument the API dlls
Write-Host "Instrumenting DLLs..."
Get-ChildItem $websiteLocation "API*.dll" | ForEach-Object {
Set-Location -Path $websiteLocation
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSInstr.exe" $_.Name
}
#set location back to start
Set-Location -Path $startPath
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCLREnv.cmd" /GlobalTraceOn
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCLREnv.cmd" /GlobalInteractionOn
#launch the profiler on the site
Write-Host "Starting the trace..."
#start:Trace - in tracing mode so we get timing data
#output - the output vsp file
#cs - cross session mode because we are profiling an IIS session
#user - give permissions to profile to everyone
#globaloff - start with capturing off so we don't get start up data.
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /start:Trace /output:$logLocation"\API.vsp" /cs /User:Everyone /globaloff
#restart IIS so the tracer can detect it
Write-Host "Resetting IIS..."
IISReset t80w103 /noforce
IISReset t80w103 /status
#output the status
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /status
#set up request for triggering api start up
Write-Host "Starting app via untraced request..."
$request = [System.Net.WebRequest]::Create("$siteUrl/api/v1/foo/1")
$request.ContentType = "application/json"
$request.Method = "GET"
#run an initial request to trigger api start up
$response = $request.GetResponse()
Write-Host $response.StatusCode
#enable capturing
Write-Host "Enabling capturing..."
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /globalon
#wait so everything can catch up
Write-Host "Pausing for $sleepTime seconds to let processes catch up..."
Start-Sleep -s $sleepTime
#run the load tester
Write-Host "Running SOAP UI load test"
#e - the site to test against
#s - the test suite name
#c - the test cases
#l - the loadtest to run
#r - log reports
#f - log location
#<soap ui test suite location>
$loadtestName = "LoadTest All API Methods"
& "C:\Program Files (x86)\SmartBear\SoapUI-5.1.3\bin\loadtestrunner.bat" -e $siteUrl -s "Load Test" -c "Load Test Cases" -l $loadtestName -r -f $logLocation "Api Profiler Load Test.xml"
#wait so everything can catch up
Write-Host "Pausing for $sleepTime seconds to let processes catch up..."
Start-Sleep -s $sleepTime
#disable capturing
Write-Host "Disabling capturing..."
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /globaloff
#shut down the profiler
Write-Host "Shutting down profiler. This may take some time..."
#shut down IIS so the /shutdown command works
IISReset t80w103 /noforce
IISReset t80w103 /status
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /shutdown
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCLREnv.cmd" /off /globaloff
#generate report summary
$date = Get-Date -format "yyyy-dd-M--HH-mm-ss"
$summaryReportLocation = "$logLocation\API $date"
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\VSPerfReport.exe" "$logLocation\Api.vsp" /SummaryFile /Output:"$summaryReportLocation"
#move items to shared location
$reportFolder = "\\shareDrive\API\Performance Reports\$date"
$profilerReport = get-childitem "$summaryReportLocation.vsps"
New-Item $reportFolder -type directory
Copy-Item $profilerReport.FullName $reportFolder
#log success
$logDate = Get-Date -format "yyyy-dd-mm HH-mm-ss"
$output = "[$logDate]" + [System.Environment]::NewLine + "Completed successfully."
$output | out-file "profiler-log.log" -append
Write-Host "Completed successfully."
}
catch{
#log error
Write-Host $_
$logDate = Get-Date -format "yyyy-dd-mm HH-mm-ss"
$output = "[$logDate]" + [System.Environment]::NewLine + $_ + [System.Environment]::NewLine
$output | out-file "profiler-log.log" -append
}
finally{
Write-Output "Done"
}
Как я уже сказал, вышеописанное работает нормально, если я сам запускаю его на DEV, но я попытался запустить его, и он работает неправильно.
Enter-PSSession DEV
Invoke-Command -ComputerName DEV -FilePath "C:\PerformanceTest\profile-tracing-SoapUI.ps1"
Exit-PSSession
- До сих пор расследование
Вышеприведенное, кажется, вызывает скрипт, но я столкнулся с некоторыми странными проблемами. Во-первых, сценарий вызывается на компьютере, на котором я выполняю сценарий (мой для тестирования, TFS Build Server для автоматического развертывания), а не на DEV, как я ожидал. Вероятно, это мое неправильное понимание PowerShell и достаточно простое исправление - все это означает, что, если я не скопирую сценарий на сетевой компьютер, с которого я тестирую (мой собственный сервер или TFS Build Server), PowerShell просто исключит ошибку каталога, не найденную прежде чем даже нажать сценарий. Во-вторых, даже если у меня есть сценарий, установленный на компьютере, с которого я пытаюсь его запустить (который находит сценарий и запускает выполнение), он всегда попадает на вывод "Запуск трассировки...", за которым следуют некоторые Информация о vsperfcmd.
Starting the trace...
Microsoft (R) VSPerf Command Version 12.0.30723 x64
Copyright (C) Microsoft Corp. All rights reserved.
Global logging control
------------------------------------------------------------
Setting global profile state to OFF.
Он висит там, и я предполагаю, что вызов не полностью выполнен, поскольку я должен выйти из него. Я оставляю его включенным в течение значительного периода времени (более 20 минут, локально требуются секунды, чтобы пройти эту точку), но оно никогда не продолжалось до этой точки. Локальный запуск вывода скрипта в этом месте выглядит следующим образом:
Starting the trace...
Microsoft (R) VSPerf Command Version 12.0.30723 x64
Copyright (C) Microsoft Corp. All rights reserved.
Global logging control
------------------------------------------------------------
Setting global profile state to OFF.
Resetting IIS...
- Эта проблема
Это приводит меня к мысли, что проблема заключается в
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /start:Trace /output:$logLocation"\API.vsp" /cs /User:Everyone /globaloff
И действительно, когда я запустил сильно укороченный скрипт для проверки этой гипотезы, я смог проверить, что элемент управления никогда не возвращается оболочке после выполнения этой команды - ниже приведен скрипт, который я запустил для подтверждения этого И его вывода:
Enter-PSSession t80w103
Invoke-Command -ComputerName t80w103 -ScriptBlock{
try{
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /start:Trace /output:$logLocation"\API.vsp" /cs /User:Everyone /globaloff
Write-Host "No Error and Control Returned"
}
catch{
Write-Host "Error!"
}
finally{
Write-Output "Done.."
}
}
Exit-PSSession
OUTPUT:
Microsoft (R) VSPerf Command Version 12.0.30723 x64
Copyright (C) Microsoft Corp. All rights reserved.
Global logging control
------------------------------------------------------------
Setting global profile state to OFF.
После этой команды ничего не печатается. Некоторое время я думал, что это должно означать, что что-то не так с приложением или сервером DEV, которые мешали ему правильно обращаться к vsperfcmd, но я проверил в диспетчере задач DEV и смог обнаружить, что VSPerfMon.exe действительно появляется в списке процессов, когда Я запускаю команду и останавливаюсь, когда вырываюсь из нее ctrl +. Таким образом, кажется, что команда работает, по крайней мере частично, она просто не возвращает управление, чтобы можно было выполнить следующую часть скрипта.
Итак, мой вопрос, как я могу заставить это работать? Почему не возвращается управление после выполнения команды запуска монитора профиля? И это лучший способ для профилирования производительности после автоматического развертывания сборки из TFS? (см. ниже для обновлений)
- Дополнительное расследование
Я продолжал исследовать это самостоятельно после публикации и узнал некоторые интересные вещи. Во-первых, я должен отметить, что версия powershell на сервере DEV устарела (чего я не осознавал), и поэтому я обновил ее до версии 3. Однажды я попытался запустить скрипт локально на powershellV3 в ISE-сборке powershell I понял, что он зависает точно так же, как когда я пытался запустить скрипт удаленно. Только когда я запускаю сценарий с помощью команды правого клика "запустить с powershell", сценарий работает, что, как я понимаю, вызывает powershellV1. Кажется, есть четкое различие в том, как разные версии powershell обрабатывают мой скрипт.
Пересмотренные вопросы: почему скрипт работает в powershellV1, но не V3? Почему V3 зависает после \start:trace, а V1 нет? И как мне получить сетевое выполнение для запуска сценария с использованием V1, когда сеансы PowerShell не были представлены до V2?
1 ответ
Ответ на пересмотренный вопрос позволил мне решить эту проблему. Оказывается (хотя я не знаю, ПОЧЕМУ, так как я не вижу исходный код), что VSPerfCMD.exe не возвращает управление оболочке в версиях Powershell после V1, когда он запускается. Это означает, что вызов
VSPerfCmd.exe /Start
должно быть сделано в отдельной оболочке, иначе скрипт будет зависать. Управление возвращается только после того, как другая оболочка вызовет команду выключения. Чтобы решить эту проблему, я использую следующий код для запуска профилировщика, а затем подожду, пока мой скрипт не запустится, прежде чем продолжить:
Write-Host "Starting the trace..."
#start:Trace - in tracing mode so we get timing data
#output - the output vsp file
#cs - cross session mode because we are profiling an IIS session
#user - give permissions to profile to everyone
#globaloff - start with capturing off so we don't get start up data. > is this the problem?
start powershell -ArgumentList "-ExecutionPolicy Bypass -command & 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe' /start:Trace /output:$logLocation\CatalogAPI.vsp /cs /User:Everyone /GlobalOff"
#wait for VSPerfCmd to start up
Write-Host "Waiting 15s for VSPerfCmd to start up..."
Start-Sleep -s 15
$counter = 0
while($counter -lt 6)
{
$e = & "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /status
if ($LASTEXITCODE -eq 0){
Write-Host $e
break
}
if ($LASTEXITCODE -eq 1 -and $counter -lt 5){
Write-Host "Still waiting. Give it 10s more ($counter)..."
Start-Sleep -s 10
$counter++
continue
}
throw "VSPerfCmd Error: $e"
}
Использование приведенного выше кода вместо исходного запуска трассировки в исходном сценарии должно позволить вам запустить автоматическое профилирование производительности с помощью сценария powershell, но я не могу гарантировать результаты, поскольку сценарий немного изменился с тех пор, как я его опубликовал (из-за различных исследований и улучшение кода). Чтобы заставить это работать, просто используйте выше вместо
#enable capturing
Write-Host "Enabling capturing..."
& "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Performance Tools\x64\VSPerfCmd.exe" /globalon
tl; dr VSPerfCmd.exe / Start не возвращает управление оболочке, которая вызывала команду, до тех пор, пока служба профилирования не будет остановлена в версиях powershell после powershell v1. Если вы хотите автоматизировать профилирование производительности, вам нужно запустить профилировщик производительности в другой оболочке, а затем дождаться, пока состояние профилировщика покажет, что он активен, прежде чем продолжить.