0

Context:
I want to run a test, and while it is running to gather CPU utilization. Basically, when I start the test I also want to start collecting data for CPU utilization, at the end of the test I also want to stop collecting data for CPU utilization (and average it afterwards).
Using PowerShell 3 on a Win7

Approach:
At the beginning of the test I added a function that starts a background job. The background job executes a scriptblock that runs a 'while ($true)' loop that collects the Average CPU utilization and saves it to a file. At the end of the test I read the file, average the collected data and stop and remove the job.

Problems:
For the current approach, the scriptblock doesn't always get executed, and no new data gets gathered. If I run the test several times, the scriptblock gets executed (in one of the runs) - I can't think of anything that would be relevant or this behavior. Also the scriptblock always gets executed if I use breakpoints anywhere in the test.
Can't think of another approach.

Answers desired:
1. Why isn't the script block being executed? and how to get it to execute?
2. Is there another way to collect resource data for the duration of the test?

Please note that the code has been through various try and fail runs and is no longer 'pretty'.
Code:

function Start-ResourceMonitor
{
    param()
    $avg=@()
    $timestamp = Get-Date -Format o | foreach {$_ -replace ":", "."}
    $compName=$env:COMPUTERNAME
    $myFilePath = Join-Path -path $env:TEMP -childpath .\temp_$timestamp.txt
    $scriptBlock = {
        $avgSB = $args[0]
        $timestampSB = $args[1]
        while ($true) {
            $avgSB += Get-WmiObject win32_processor -computername $args[2] | Measure-Object -property LoadPercentage -Average | Foreach {$_.Average}            
            $avgSB | Out-File -FilePath $args[3] -Force
            start-sleep -Milliseconds 500 
        }
    }
    $res = start-job -RunAs32 -Name 'MyJob' -ScriptBlock $scriptBlock -ArgumentList @($avg,$timestamp,$compName,$myFilePath)
    while (($res.State -ne 'Running') -or (-not (Test-Path $myFilePath))){
        start-sleep -Milliseconds 500
    }
}

[other test functions]

function Stop-ResourceMonitor
{
    param()
    Stop-Job -Name MyJob
    $testFN = Get-ChildItem -Path $env:TEMP -Filter "temp*.txt" | sort LastWriteTime -Descending | select -First 1
    $global:CPU_Average = get-content -path $testFN.FullName
    $global:CPU_Average | Measure-Object -Average | select -ExpandProperty Average | Write-Host
    Remove-Job -Name MyJob
}
Mircea
  • 178
  • 1
  • 1
  • 6
  • I'm gussing that the `Job` is added, but execution hasn't yet started (i.e. `State` hasn't changed to `Running`). When you create a `Job` you give up handling the scheduling of the `Scriptblock`. Since you are dependent on waiting until the `Job` has started executing, couldn't you just, as the last line in `Start-ResourceMonitor` sleep in a loop until the `State`of the `Job` has changed to `Running`? – Robert Westerlund May 09 '14 at 09:44
  • @robert.westerlund I've tried out your suggestion, but get the same results. For debug purposes (local version only), I output the job state before stopping it, in Stop-ResourceMonitor - in all my runs so far the state has been running. – Mircea May 09 '14 at 12:16
  • Yes, but the `Job` could have started running just before you stop it, that is after you have performed the actual test. If you output the state of the job right before you perform the actual test I'm guessing it is not in `Running` state. Again, I'm just guessing, since I'm not sure how exactly that you determine that "the scriptblock doesn't always get executed". – Robert Westerlund May 09 '14 at 12:31
  • @robert.westerlund Clarification - In addition to my own debug version state output, I also added the sleep loop in the Start-ResourceMonitor as you suggested, and got the same results. One way to determine the scriptblock isn't executed is by checking the C:\TEMP folder for the new file that should be created for each run. – Mircea May 09 '14 at 12:38
  • Just to make sure I understand, the sleep loop you tried including, was it placed on the line immediately following `$res = Start-Job ...` and did it look similar to `while ($res.State -ne 'Running'){ Start-Sleep -Milliseconds 500 }`? Also, for how long does your test process run? I'm guessing it is running at least several seconds, is that guess correct? Remember that the `Get-WmiObject -ComputerName ...` might take a short while to get a response too, so if the test is finished either before the `Job` starts or quicker than the `Get-WmiObject` call completes you will not get a file written. – Robert Westerlund May 09 '14 at 13:13
  • @robert.westerlund The test was running anywhere between 2 to 60 seconds. Following your hint I modified the while as to include a condition for the file existing and wait for it's creation. This has solved the problem. I've updated the code so as to show the working version. Thanks so much for the patience and help. – Mircea May 09 '14 at 14:16
  • Glad you got it working! Please write an explanation of the final solution as an answer and accept your own answer. This will mark the question as answered, and will also make it easier for anyone with a similar problem to identify what was the solution to your problem. – Robert Westerlund May 09 '14 at 17:30

1 Answers1

0

The underlying problem was that the test was running for anywhere between 2 and 60 seconds, and there where runs where the Get-WmiObject was taking longer than the test run, and thus the code for creating the file with the collected metrics wasn't executed for those runs.
Adding a wait loop, after starting the job, (while and sleep) for the job to actually start running and the files to get created solved the problem.
The code from the question has also been updated with the solution.

Mircea
  • 178
  • 1
  • 1
  • 6