使用 Powershell 自动化 VSPerfCmd.exe(Visual Studio 性能分析)

Using Powershell to Automate VSPerfCmd.exe (Visual Studio Performance Profiling)

因为这个问题是我尝试解决问题的尝试,所以我在下面的 post 中包含了重要的背景和调查历史。因此,我将 post 分为 3 个部分:"Background"、"Investigation So Far" 和 "Problem"。当我自己进行更多调查时,我还添加了一个 "Additional Investigation" 部分来扩展我的发现。这最终导致我自己解决了这个问题,并且我将原始的 post 作为任何尝试做同样事情的开发人员的指南。该问题最终成为 return 控制问题 VSPerfCmd.exe 在 v1.

之后的 Powershell 版本中出现
  1. Background

作为我的 TFS 构建(包括自动部署到我的开发 Web 服务器 DEV)的一部分,我想开始 运行 我的代码的性能测试,以便我可以看到什么时候有新的变化对我 API 的速度产生负面影响。为此,我在 DEV 上安装了 test-运行ner (SoapUI) 和 VS Team Tools,并编写了一个 Powershell 脚本,当 运行 local 在 DEV 上,生成我想要的报告。但是,我还无法触发该脚本并让它在任何其他位置运行。我的意思是说,只有登录到服务器,找到 .ps1 文件并在 Powershell 中 运行 将其 shell 才能工作。这是该脚本:

#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. The Investigation So Far

以上似乎确实触发了脚本,但我 运行 遇到了一些奇怪的问题。首先,脚本是在我执行脚本的机器上调用的(我的用于测试,TFS 构建服务器用于自动部署)而不是像我期望的那样在 DEV 上调用。这可能是我对 powershell 的误解和一个足够简单的修复 - 这意味着,除非我将脚本复制到我正在测试的网络机器(我自己的或 TFS 构建服务器)powershell 甚至在点击脚本之前就吐出一个找不到目录的错误。其次,即使我在机器上安装了脚本我试图从中触发它(确实找到脚本并开始执行)它只会到达输出 "Starting the trace..." 后跟一些关于 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.

它挂在那里,我认为调用没有完全执行,因为我必须中断它。我已经离开它 运行ning 很长一段时间(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. The Problem

这让我相信问题所在是

& "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

事实上,当我 运行 一个大大缩短的脚本来测试这个假设时,我能够验证在执行该命令后控件从未 returned 到 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 退出它时停止。所以看起来命令 工作,至少部分地,它只是不 return 控制以便可以执行脚本的下一部分。

所以,我的问题是,我怎样才能让它发挥作用?为什么启动profile monitor的命令执行后不控制return?这是从 TFS 自动部署构建后进行性能分析的最佳方法吗? (更新见下文)

  1. Additional Investigation

自从 posting 以来,我一直在自己研究这个问题,并且学到了一些有趣的东西。首先,我应该注意到 DEV 服务器上的 powershell 版本已经过时(我没有意识到这一点),所以我将它更新到版本 3。一旦我尝试 运行在 powershellISE 中的 powershellV3 上本地编写脚本我意识到它挂在 exact 中的方式与我尝试 运行 时的方式相同脚本远程。只有当我通过右键单击命令 "run with powershell" 运行 脚本时,我意识到调用 powershellV1 的脚本才能工作。不同版本的 powershell 处理我的脚本的方式似乎存在明显差异。

修改问题:为什么脚本在powershellV1中有效,但在V3中无效?为什么 V3 在 \start:trace 之后挂起而 V1 没有?当 powershell 会话直到 V2 才引入时,如何使用 V1 网络执行 运行 脚本?

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

VSPerfCmd.exe /Start

必须在单独的 shell 中完成,否则脚本将挂起。控件仅 return 编辑一次,另一个 shell 调用关闭命令。为了解决这个问题,我使用以下代码启动分析器,然后等待我的脚本,直到它 运行ning 后再继续:

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