Start-Job of Get-Counter paths from array - powershell

As a follow up to my last question I would like to compile a list of perfmon counters that are spawned and continuously sampled(-Continuous) at 1 second interval (default) with Start-Job, sleep for 60 seconds then run Receive-job to get the past 60-seconds of stats (-Average, -Sum, -Minimum, -Maximum).
The problem I'm having now is the job starts and stays in a "running" state when I use then absolute counter path. If I try to iterate through an array of performance counter strings the job state goes to "Completed".
Here are two code samples that show non-working and working results.
Doesn't work. Job state Completes even though -Continuous is set. No errors are raised.
$jobs=#{}
$counters=#("\Processor(*)\% Processor Time",
"\Network Interface(*)\Bytes Received/sec",
"\Network Interface(*)\Bytes Sent/sec")
foreach ($counterPath in $counters) {
$job=Start-Job {get-counter -Counter "$counterPath" -Continuous | foreach {$_.CounterSamples} }
$jobs[$job.id]=$counterPath
}
Works as expected, but doesn't allow multiple counters to start-job via a loop.
$jobs=#{}
$job=Start-Job {get-counter -Counter "\Processor(*)\% Processor Time" -Continuous | foreach {$_.CounterSamples} }
$jobs[$job.id]=$counter
Resulting Output
PS C:\Users\msnow> $jobs=#{}
[string]$counter="\Processor(*)\% Processor Time"
$job=Start-Job {get-counter -Counter "$counter" -Continuous | foreach {$_.CounterSamples} }
$jobs[$job.id]=$counter
__________________________________________________________________________________________________________________________
PS C:\Users\msnow> $jobs=#{}
$job=Start-Job {get-counter -Counter "\Processor(*)\% Processor Time" -Continuous | foreach {$_.CounterSamples} }
$jobs[$job.id]=$counter
__________________________________________________________________________________________________________________________
PS C:\Users\msnow> Get-Job
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
1 Job1 Completed True localhost get-counter -Counter "...
3 Job3 Running True localhost get-counter -Counter "...
__________________________________________________________________________________________________________________________
PS C:\Users\msnow> receive-job -id 3 | measure CookedValue -sum -Average
Count : 11466
Average : 5.20268509822716
Sum : 59653.9873362726
Maximum :
Minimum :
Property : CookedValue

I believe the problem is in the scope of the variable $counter. Start-job runs the scriptblock in other runspace and the variable it is not visible. You need to pass it as -argumentlist .try:
Start-Job {get-counter -Counter "$($args[0])" -Continuous | foreach {$_.CounterSamples} } -ArgumentList $counter
or
Start-Job {param($counter) ; get-counter -Counter "$($counter)" -Continuous | % {$_.CounterSamples} } -ArgumentList $counter

Related

Why does my Powershell IF...Else loop always do the ELSE statement, no matter what the variable result is?

I am making a simple script for my internship in powershell, that uses simple stuff like variables, the IF...ELSE statement, as well as Get-Counter.
$CpuLoad = "\Processor(_Total)\% Processor Time"
$Threshold = 50
Get-Counter -Counter $CpuLoad -SampleInterval 1 -MaxSamples 10
IF($CpuLoad -gt $Threshold) {
Write-Host "CPU Utilizacija ir lielaka par 50 procentiem!"
} Else {
Write-Host "Viss ok!"
}
That is the script. No matter what the CPU utilization percentage is, it will always say the ELSE write-host statement, not the IF write-host statement. Do not worry. I am not trying to cheat or anything of the sort ;). I'm simply dumb-founded as to how such a simple script can break so easily! Any help is appreciated!
Comparing the string "\Processor(_Total)\% Processor Time" to the number 50 doesn't make much sense.
Instead, use Where-Object to test if any of the counter samples returned by Get-Counter exceeds the threshold:
$CpuLoad = "\Processor(_Total)\% Processor Time"
$Threshold = 50
$samplesExceedingThreshold = Get-Counter -Counter $CpuLoad -SampleInterval 1 -MaxSamples 10 |ForEach-Object CounterSamples |Where-Object CookedValue -gt $threshold |Select-Object -First 1
if($samplesExcheedingThreshold){
Write-Host "CPU Utilizacija ir lielaka par 50 procentiem!"
} else {
Write-Host "Viss ok!"
}

Retrieve average CPU-workload with Get-Counter

I am looking for a way to retrieve the average CPU-workload with PowerShell.
At the beginning of a script, I want to start the tracking of the CPU-workload and when it is finished I want to get the average CPU-workload between. (like 71.5%)
After a research on the web I started using a (PowerShell-)"job" for this purpose, but I was not able to get it working. This is the reduced code I have till now:
$JobObject = Start-Job -Name "MyJob" -ScriptBlock {
Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -Continuous
}
Start-Sleep -Seconds 5
$Result = Receive-Job -Job $JobObject
I was not able to simple get the average of captured values.
How to get this working?
Thank you
Edit: It is not a requirement by me to make use of jobs.
You could calculate the average separately after receiving the job:
$JobObject = Start-Job -Name "MyJob" -ScriptBlock {
Get-Counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -Continuous
}
Start-Sleep -Seconds 5
$Result = Receive-Job -Job $JobObject
$Result.Readings | Foreach-Object { ($_ -split ':')[-1].Trim() } |
Measure-Object -Average | Select-Object #{n='Average';e={"{0:f2}" -f $_.Average}}
Explanation:
$Result.Readings contains all of your sample readings in string format. You will have to parse the CPU percentage from that.
-split ':' creates an array by separating the input string by :. The resulting array contains as many elements as there are : characters. Since we only want the number after the final :, index [-1] is used.
The format operator -f requires a specific syntax. The {0} syntax represents a placeholder for the first object after the -f. {1} would represent the second object. A trivial example would be "{0}=={1}" -f $var1,$var2, which will output string versions of $var1 and $var2 separated by a ==. The {0:f2} tells PowerShell to format the first object (indicated by 0) using a fixed decimal (indicated by f) to two places (indicated by 2).

Reduce output from three commands to one line

I have this script:
$counterWS = "\Process(powershell)\Working Set"
$counterWSPe = "\Process(powershell)\Working Set Peak"
$counterWSPr = "\Process(powershell)\Working Set - Private"
$dataWS = Get-Counter -Counter $counterWS
$dataWSPe = Get-Counter -Counter $counterWSPe
$dataWSPr = Get-Counter -Counter $counterWSPr
$dataWS.countersamples | Format-Table Timestamp,#{Name='WorkingSet';Expression={($_.CookedValue/1KB)}},WorkingSetPeak,WorkingSetPrivate -Auto | findstr Timestamp
$dataWS.countersamples | Format-Table Timestamp,#{Name='WorkingSet';Expression={($_.CookedValue/1KB)}},WorkingSetPeak,WorkingSetPrivate -Auto | findstr [-]
while ($true) {
$dataWS.countersamples | Format-Table Timestamp,#{Name='WorkingSet';Expression={($_.CookedValue/1KB)}},WorkingSetPeak,WorkingSetPrivate -Auto | findstr [:]
$dataWSPe.countersamples | Format-Table Timestamp,WorkingSet,#{Name='WorkingSetPeak';Expression={($_.CookedValue/1KB)}},WorkingSetPrivate -Auto | findstr [:]
$dataWSPr.countersamples | Format-Table Timestamp,WorkingSet,WorkingSetPeak,#{Name='WorkingSetPrivate';Expression={($_.CookedValue/1KB)}} -Auto | findstr [:]
Start-Sleep -s $args[0]
}
and the result is like:
Timestamp WorkingSet WorkingSetPeak WorkingSetPrivate
--------- ---------- -------------- -----------------
29/07/2016 18:41:12 10644
29/07/2016 18:41:13 10676
29/07/2016 18:41:14 3056
Is there a way to reduce to make output like this:
Timestamp WorkingSet WorkingSetPeak WorkingSetPrivate
--------- ---------- -------------- -----------------
29/07/2016 18:41:12 10644 10676 3056
Collect all 3 counters at once (the parameter -Counter accepts a list of arguments), then split the result into separate calculated properties:
$ws = '\Process(powershell)\Working Set'
$wsPeak = '\Process(powershell)\Working Set Peak'
$wsPriv = '\Process(powershell)\Working Set - Private'
Get-Counter -Counter $ws, $wsPeak, $wsPriv |
Select-Object Timestamp,
#{n='WorkingSet';e={$_.CounterSamples[0].CookedValue / 1KB}},
#{n='WorkingSetPeak';e={$_.CounterSamples[1].CookedValue / 1KB}},
#{n='WorkingSetPrivate';e={$_.CounterSamples[2].CookedValue / 1KB}}
The CounterSamples property is a list of PerformanceCounterSample objects that can be accessed individually by index.
If you don't want to rely on the results being returned in the order of the parameters passed to Get-Counter you can select them by their path, e.g. like this:
#{n='WorkingSetPeak';e={
($_.CounterSamples | Where-Object { $_.Path -like '*peak' }).CookedValue / 1KB
}}
For continuous sample collection add the parameters -Continuous and -SampleInterval to Get-Counter. A loop is not required.
$interval = 5 # seconds
Get-Counter -Counter $ws, $wsPeak, $wsPriv -Continuous -SampleInterval $interval |
Select-Object ...

Powershell Get a specific process counter with id process

I want to get specific counters for processes that I have process id's for. However I can't think of a way to use where-object to match the process for the counter.
Like
Where Gc '\process(*)\id process -eq 456 gc '\process($name)\working set'
So use the process id to retrieve the name and get the working set (or something to that effect).
It seems a bit convoluted to get the correct performance counter path for a process with multiple instances of the same process name:
$proc_id=6580
$proc_path=((Get-Counter "\Process(*)\ID Process").CounterSamples | ? {$_.RawValue -eq $proc_id}).Path
Get-Counter ($proc_path -replace "\\id process$","\% Processor Time")
Timestamp CounterSamples
--------- --------------
11/20/2014 5:39:15 PM \\myhost\process(conhost#2)\% processor time :
0
You can get counters for a process name so first get the process name by using its Id and then embed the process name in the counter. For example:
$id = # your process id
$proc = (Get-Process -Id $id).Name
Get-Counter -Counter "\Process($proc)\% Processor Time"
If you want a solution that also include process with multiple instance IDs you can use :
$p = $((Get-Counter '\Process(*)\ID Process' -ErrorAction SilentlyContinue).CounterSamples | % {[regex]$a = "^.*\($([regex]::Escape($_.InstanceName))(.*)\).*$";[PSCustomObject]#{InstanceName=$_.InstanceName;PID=$_.CookedValue;InstanceId=$a.Matches($($_.Path)).groups[1].value}})
# In french, use '\processus(*)\id de processus' for the counter name
$id = # your process id
$p1 = $p | where {$_.PID -eq $id}
Get-Counter -Counter "\Process($($p1.InstanceName+$p1.InstanceId))\% Processor Time"
# In french, use "\Processus($($p1.InstanceName+$p1.InstanceId))\% temps processeur" for the counter name
Or if you avoid to use Get-Counter and wait the sample interval, try use WMI:
$id = YourProcessIdHere
(gwmi -class Win32_PerfRawData_PerfProc_Process -Namespace "root\CIMV2" | ? {$_.IdProcess -eq $id}).Name;
It is possible to obtain some performance information with the Get-Process commandlet directly and avoid the need to resolve an instance ID.
For the case of the memory working set, just filter the output for the process id you want using where-object, then select the parameters you're interested in:
get-process | where-object{ $_.id -eq 456 } | select name,workingset

Wrong time format returned by get-counter/export-counter in Powershell

The get-counter/export-counter cmdlets in powershell seem to return dates in the US format which is rather undesirable in this case. I went through both get-help -full pages and couldn't find anything that allows me to set the date/time format. Is there another way to do this that I am not aware of, or am I stuck with the US date format?
It works so because the culture of your host is "en-US" have a look to :
Get-Culture | Format-List *
You can change the culture during a Scriptblock execution look at this code
[System.Globalization.CultureInfo] $culture = "en-US"
$a = { [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
[System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture
get-counter -Counter "\Processeur(_Total)\% temps processeur" -SampleInterval 2 -MaxSamples 3
}
&$a
[System.Globalization.CultureInfo] $culture = "fr-FR"
$a = { [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture
[System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture
get-counter -Counter "\Processeur(_Total)\% temps processeur" -SampleInterval 2 -MaxSamples 3
}
&$a
There are a lots of information about that in Powershell-cookbook
You can always re-format the output:
get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 2 -MaxSamples 3 |
select #{l="Timestamp";e={([datetime]"$($_.timestamp)").tostring("yyyy/MM/dd HH:mm:ss")}},Readings | fl
Timestamp : 2011/06/21 18:33:09
Readings : \\TMA-1\processor(_total)\% processor time :
3.87658516403437
Timestamp : 2011/06/21 18:33:11
Readings : \\TMA-1\processor(_total)\% processor time :
1.93861060616496
Timestamp : 2011/06/21 18:33:13
Readings : \\TMA-1\processor(_total)\% processor time :
3.10139633471207