因为这个问题是我试图解决更大的问题,所以我在下面的帖子中包含了重要的背景和调查历史。因此,我将帖子分为 3 个部分:“背景”、“迄今为止的调查”和“问题”。当我自己进行更多调查时,我还添加了一个“额外调查”部分来扩展我的发现。这最终导致我自己解决了这个问题,并且我已经将原始帖子作为任何尝试做同样事情的开发人员的指南。该问题最终成为 VSPerfCmd.exe 在 v1 之后的 Powershell 版本中的控制权返回问题。
- 背景
作为我的 TFS 构建(包括自动部署到我的开发 Web 服务器 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 构建服务器用于自动部署),而不是我期望的在 DEV 上。这可能是我对 powershell 的误解和一个足够简单的修复 - 所有这些意味着,除非我将脚本复制到我正在测试的网络机器(我自己的或 TFS 构建服务器)powershell 只是 barfs a directory not found 错误甚至在击中脚本之前。其次,即使我在尝试触发它的机器上安装了脚本(它确实找到了脚本并开始执行),它也只会到达输出“开始跟踪......”,然后是一些有关 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
事实上,当我运行一个大大缩短的脚本来测试这个假设时,我能够验证执行该命令后控制永远不会返回到 shell - 下面是我运行以确认这一点的脚本及其输出:
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+break 时停止。因此,该命令似乎正在工作,至少在某种程度上,它只是没有返回控制权,因此可以执行脚本的下一部分。
所以,我的问题是,我怎样才能让它工作?为什么执行启动配置文件监视器的命令后控制不返回?这是从 TFS 自动部署构建后进行性能分析的最佳方法吗?(见下文更新)
- 额外调查
自从发布以来,我一直在自己进行调查,并学到了一些有趣的东西。首先,我应该注意到 DEV 服务器上的 powershell 版本已经过时(我没有意识到),所以我将它更新到版本 3。一旦我尝试在 powershell ISE 中的 powershellV3 上本地运行脚本,我意识到它挂起的方式与我尝试远程运行脚本时完全相同。只有当我通过右键单击命令“使用 powershell 运行”运行脚本时,我意识到调用 powershellV1 的脚本才能工作。不同版本的 powershell 处理我的脚本的方式似乎存在明显差异。
修改后的问题:为什么脚本在 powershellV1 而不是 V3 中工作?为什么 V3 在 \start:trace 之后挂起,而 V1 没有?当直到 V2 才引入 powershell 会话时,我如何获得网络执行以使用 V1 运行脚本?