2

因为这个问题是我试图解决更大的问题,所以我在下面的帖子中包含了重要的背景和调查历史。因此,我将帖子分为 3 个部分:“背景”、“迄今为止的调查”和“问题”。当我自己进行更多调查时,我还添加了一个“额外调查”部分来扩展我的发现。这最终导致我自己解决了这个问题,并且我已经将原始帖子作为任何尝试做同样事情的开发人员的指南。该问题最终成为 VSPerfCmd.exe 在 v1 之后的 Powershell 版本中的控制权返回问题。

  1. 背景

作为我的 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
  1. 迄今为止的调查

以上似乎确实触发了脚本,但我遇到了一些奇怪的问题。首先,脚本是在我执行脚本的机器上调用的(我的用于测试,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...
  1. 问题

这让我相信问题线是

& "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 自动部署构建后进行性能分析的最佳方法吗?(见下文更新)

  1. 额外调查

自从发布以来,我一直在自己进行调查,并学到了一些有趣的东西。首先,我应该注意到 DEV 服务器上的 powershell 版本已经过时(我没有意识到),所以我将它更新到版本 3。一旦我尝试在 powershell ISE 中的 powershellV3 上本地运行脚本,我意识到它挂起的方式与我尝试远程运行脚本时完全相同。只有当我通过右键单击命令“使用 powershell 运行”运行脚本时,我意识到调用 powershellV1 的脚本才能工作。不同版本的 powershell 处理我的脚本的方式似乎存在明显差异。

修改后的问题:为什么脚本在 powershellV1 而不是 V3 中工作?为什么 V3 在 \start:trace 之后挂起,而 V1 没有?当直到 V2 才引入 powershell 会话时,我如何获得网络执行以使用 V1 运行脚本?

4

1 回答 1

0

回答修改后的问题使我能够解决这个问题。事实证明(虽然我不知道为什么,因为我看不到源代码)VSPerfCMD.exe 在 V1 之后的 Powershell 版本中在启动时不会将控制权返回给 shell。这意味着调用

VSPerfCmd.exe /Start

必须在单独的 shell 中完成,否则脚本将挂起。仅当另一个 shell 调用 shutdown 命令时才返回控制权。为了解决这个问题,我使用以下代码启动分析器,然后等待我的脚本运行,然后再继续:

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 不会将控制权返回给调用该命令的 shell,直到在 powershell v1 之后的 powershell 版本中停止分析服务。如果您希望自动执行性能分析,您需要在另一个 shell 中启动性能分析器,然后等到分析器状态表明它处于活动状态,然后再继续。

于 2015-07-30T18:48:48.957 回答