The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,641 other followers

Archive for the ‘Scripting’ Category

PowerShell: the issues you bump into when using Get-Counter and Measure-Object aggregation

Posted by jpluimers on 2019/07/17

Single process data

You can get many process properties using , for instance the total running time in minutes [WayBack] Get-Process:

New-TimeSpan -Start (Get-Process "devenv").StartTime | Select-Object -ExpandProperty "TotalMinutes"

Actually, the it matches multiple process instances, then you need to aggregate, for instance using this:

Get-Process "cmd" | ForEach-Object { New-TimeSpan -Start $_.StartTime } | Measure-Object -Property "TotalMinutes" -Sum | Select-Object -ExpandProperty "Sum"

If you want to see which properties are available, then use either of these:

Get-Process | Select-Object * | Out-GridView
Get-Process | Get-Member

Getting aggregated process CPU usage times

Lets start getting the total CPU time of all processes by using Get-Process which – without arguments – returns data for all processes it has access to:

PS C:\Users\Developer> Get-Process | Measure-Object -Property CPU -Sum

You’d think this gives only the sum. Wrong:

Count : 80
Average :
Sum : 2410.5625
Maximum :
Minimum :
Property : CPU

Ensure you get data for all processes

And you need it with an Administrative UAC token in order to get all processes on the system. It doesn’t show many more processes, but a lot more CPU time since boot:

Count : 83
Average :
Sum : 6078.125
Maximum :
Minimum :
Property : CPU

Showing one aggregate

So how to get rid of [WayBack] Measure-Object showing all aggregates?

The only stable solution I found is to expand that into | Measure-Object -Property CPU -Sum | Select-Object -ExpandProperty Sum which I found in the answer by davidhigh at [WayBack] Listing processes by CPU usage percentage in powershell – Stack Overflow and got me this by using [WayBack] Select-Object.

Note you have to use -ExpandProperty as just -Propertywill not cut it as you get an empty result (I think this has to do with Sum being a nullable type: Sum Property System.Nullable[double]):

PS C:\Windows\system32> Get-Process | Measure-Object -Property CPU -Sum | Select-Object -Property Sum
PS C:\Windows\system32> Get-Process | Measure-Object -Property CPU -Sum | Select-Object -ExpandProperty Sum
6126.40625

CPU % is hard

You’d think a CPU % is an easy thing to measure as it has been available in the Task Manager for such a long time, but it’s harder to determine than for instance process memory. Let me try to explain that (please post comments when it is unclear).

For process memory, you can measure it at the current instant: if you freeze the CPU, you can count the number of bytes used by the process.

For relative CPU usage however, you need to measure how a process executes over time. During that time, CPU usage may (or will) vary a little bit, so you need to measure over a little time-delta. If CPU core frequencies vary over time, you need to adjust for that so it gets even more difficult. Two links with a bit more background information on this:

I think the above difference is the reason that Get-Process doesn’t return a CPU percentage.

Performance Counters using Get-Counter

Instead, you can use [WayBackGet-Counter to get processor CounterSampleslike this:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples

It gets you the % Processor Time which is the percentage of CPU determined by a CPU [WayBackHardware performance counter.

Filtering results

If you want this for a specific process, you first need to find out which processes exist. You can do that by getting the unique InstanceName property values by using [WayBack] Sort-Object:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Select-Object -Property InstanceName | Sort-Object -Property InstanceName -Unique

(Here you do not need the -ExpandProperty on Select-Objectas the -Property suffices)

Special InstanceName values

There are always entries with InstanceName having values _total and idle:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "_total" }
(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "idle" }

You can even get both at the same time by using the -or operator with [WayBackWhere-Object:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {($_.InstanceName -eq "_total") -or ($_.InstanceName -eq "idle") }

Since the outputs are CPU core based, and sampled over time, they can be above 100%, heck even above the 100% * amount-of-CPU-cores as seen on a 2-core CPU system:

Path                                                InstanceName  CookedValue
----                                                ------------  -----------
\\w81entx64vs2015\process(idle)\% processor time    idle          186.168932138452
\\w81entx64vs2015\process(_total)\% processor time  _total        200.016208082634

You see that the CookedValue is what you’re after. If you just want the value for one instance, just use the -ExpandProperty trick shown above:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "_total" } | Select-Object -ExpandProperty CookedValue

Accommodating for multi-instance processes

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "svchost" } | Select-Object -ExpandProperty CookedValue

This gets you a list like this:

PS C:\Windows\system32> (Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "svchost" } | Select-Object -ExpandProperty CookedValue
0
0
...
0
0

For that, you need to aggregate by using Measure-Object like described in the Get-Process section:

PS C:\Windows\system32> (Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "svchost" } | Measure-Object -Property CookedValue -Sum | Select-Object -ExpandProperty Sum
0

A more elaborate example of aggregating is at [Archive.isExportPerfomanceCountersHelpers.ps1.

Aggregated output usually works

This works for a single-instance process as well:

(Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "lsass" } | Measure-Object -Property CookedValue -Sum | Select-Object -ExpandProperty Sum

Example output:

PS C:\Windows\system32> (Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "lsass" } | Measure-Object -Property CookedValue -Sum | Select-Object -ExpandProperty Sum
0

The dreaded error

The above often works, but sometimes you get an error, often even earlier in the process.

The error is always like Get-Counter : The data in one of the performance counter samples is not valid. View the Status property for each PerformanceCounterSample object to make sure it contains valid data.:

PS C:\Windows\system32> (Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.InstanceName -eq "lsass" } | Measure-Object -Property CookedValue -Sum | Select-Object -ExpandProperty Sum
Get-Counter : The data in one of the performance counter samples is not valid. View the Status property for each PerformanceCounterSample object to make sure it contains valid data.
At line:1 char:2
+ (Get-Counter '\Process(*)\% Processor Time').CounterSamples | Where-Object {$_.I ...
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidResult: (:) [Get-Counter], Exception
    + FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.GetCounterCommand

0

It took me a while to figure this out, but the root cause is that in-between Get-Counter retrieving the hardware process counters and processing the values, the CPU continues executing thereby changing some of the values. For instance, processes can quit (the CPU is not aware of processes, so binding CPU thread information from the hardware process counters to logical processes can fail in that step).

These links helped me tremendously as the -ErrorAction SilentlyContinue is not documented by Microsoft at Get-Counter:

Summary

Always use a construct like this to search for CPU usages on one or more processes named powershell:

(Get-Counter '\Process(*)\% Processor Time' -ErrorAction SilentlyContinue).CounterSamples | Where-Object {$_.InstanceName -eq "powershell" } | Measure-Object -Property CookedValue -Sum | Select-Object -ExpandProperty Sum

–jeroen

Posted in CommandLine, Development, PowerShell, PowerShell, Scripting, Software Development | Leave a Comment »

Design Patterns & Refactoring

Posted by jpluimers on 2019/07/16

Design Patterns and Refactoring articles and guides. Design Patterns video tutorials for newbies. Simple descriptions and full source code examples in Java, C++, C#, PHP and Delphi.

Source: [WayBackDesign Patterns & Refactoring.

And indeed a lot of examples in Delphi too; few sites have that: Delphi site:sourcemaking.com.

–jeroen

Via: [WayBack] I stumbled upon this yesterday, very informative, accessible and also with Delphi examples – among other languages. – Steffen Nyeland – Google+

Posted in Delphi, .NET, Software Development, Development, C#, Java, Scripting, PHP, C++, C, Design Patterns, Java Platform | Leave a Comment »

PowerShell: $args variable outer scope has command-line arguments – about_Automatic_Variables | Microsoft Docs

Posted by jpluimers on 2019/07/16

I always forget the exact syntax for getting command-line arguments to PowerShell. It’s the $args implicit array variable:

–jeroen

Posted in CommandLine, Development, PowerShell, PowerShell, Scripting, Software Development | Leave a Comment »

Tech Notes: TypeScript at Google

Posted by jpluimers on 2019/07/11

For my link archive: [WayBack] Tech Notes: TypeScript at Google.

A good discussion, also about alternatives (like Kotin, Scala, GTW) is at [WayBack] TypeScript at Google | Hacker News

Via [WayBack] TypeScript at Google https://news.ycombinator.com/item?id=17894764 #typescript #google #microsoft #javascript – Adrian Marius Popa – Google+

–jeroen

Posted in Development, JavaScript/ECMAScript, Scripting, Software Development, TypeScript | Leave a Comment »

PowerShell: get WindowsUpdate information

Posted by jpluimers on 2019/07/11

A while back, I needed to check Windows Update information on a few hosts, so I wanted to script it. Below are a few links that helped me solve this started.

Note: For Windows Update, you need the TiWorker.exe process, which can consume a lot of CPU. See DISM fix for Windows 8.1 high CPU usage of TiWorker.exe which is basically the same for all Windows versions since 8.0.

The infrastructure management on that site was ehm, a bit lacking, so PowerShell modules were out, heck even PowerShell itself was initially problematic (it needed running of unsigned sources.

A few notes on the above links.

Using Microsoft.Update.AutoUpdate

This gets the last date that anything was done (query, actual update, download) on Windows Updates, but does not guarantee the installation date; on some systems it does not even return a result:

$windowsUpdateObject = New-Object -ComObject Microsoft.Update.AutoUpdate
$windowsUpdateObject.Results

This one works better though:

$windowsUpdateObject = New-Object -ComObject Microsoft.Update.AutoUpdate
$windowsUpdateObject.Results.LastInstallationSuccessDate

Based on that, you can get the number of days like this:

(New-TimeSpan -Start $windowsUpdateObject.Results.LastInstallationSuccessDate.Date -End (Get-Date)).Days

Using Get-HotFix

Though some people report that InstalledOn can be empty, I’ve hardly that happen with Get-HotFix. The easiest way to get around that is filtering with | Where-Object InstalledOn -ne $null

The cool thing with Get-HotFix is that you can filter on the kind of security update, so this gets the moment the last security update got installed:

(Get-HotFix -Description "Security Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn

And this the number of days since the last security update got installed:

(New-TimeSpan -Start (Get-HotFix -Description "Security Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days

Step by step:

Get-HotFix -Description "Security Update"

Gets all the security updates.

| Where-Object InstalledOn -ne $null

Filter out entries having an empty InstalledOn.

Sort-Object InstalledOn -Descending

Get the most recent on the top.

| Select-Object -First 1

Select only the top entry.

(Get-HotFix -Description "Security Update"...).InstalledOn

Get only the InstalledOn property.

Get-Date

Get the current timestamp consisting of date and time.

New-TimeSpan -Start (...).InstalledOn -End (Get-Date)

Get a TimeSpan over a start and end timestamp.

(New-TimeSpan ...).Days

Get the Days property of a TimeSpan.

You can do the same for regular updates by changing the -Description parameter:

(Get-HotFix -Description "Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn
(New-TimeSpan -Start (Get-HotFix -Description "Update" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days

The Description values I found are these:

PS C:\Users\Developer> Get-HotFix | Sort-Object -Unique Description | Select-Object Description

Description
-----------
Hotfix
Security Update
Update

Ironically, since the command is called Get-HotFix, the Hotfix entries on my various Windows systems have been a  long long time ago:

(New-TimeSpan -Start (Get-HotFix -Description "Hotfix" | Where-Object InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days

When writing this in 2017, on Windows 8.1, this was more than 600 days, Windows 7 more than 400 days and Windows 10 did not have any Hotfix entries.

Old PowerShell versions

On PowerShell 2 and older, you get an error containing “Where-Object : Cannot bind parameter ‘FilterScript'”:

Where-Object : Cannot bind parameter 'FilterScript'. Cannot convert the "InstalledOn" value of type "System.String" to type "System.Management.Automation.ScriptBlock".
At line:1 char:48
+ (New-TimeSpan -Start (Get-HotFix | Where-Object <<<< InstalledOn -ne $null | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days
+ CategoryInfo : InvalidArgument: (:) [Where-Object], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.WhereObjectCommand

You solve it like this:

(New-TimeSpan -Start (Get-HotFix | Where-Object { $_.InstalledOn -ne $null } | Sort-Object InstalledOn -Descending | Select-Object -First 1).InstalledOn -End (Get-Date)).Days

By now code has become almost unreadable, so you can split it using backtick ` characters:

( `
New-TimeSpan -Start `
  ( `
    Get-HotFix | Where-Object { $_.InstalledOn -ne $null } `
    | Sort-Object InstalledOn -Descending `
    | Select-Object -First 1 `
  ).InstalledOn `
  -End (Get-Date)`
).Days

One more thing

On non-English Windows systems, the InstalledOn might actually be in the future, as you can view this happening by this simple command which I ran on 2017-11-02 :

Get-HotFix | Out-GridView

You solve it by adding a filter:

Get-HotFix | Where-Object InstalledOn -lt (Get-Date) | Out-GridView

If you run them from a script (like a batch file Get-HotFix ^| Out-GridView or ps1 file Get-HotFix | Out-GridView), then the grid-view will pop-up and immediately close because the PowerShell process ends. In that case, you need to change your scripts to add the -Wait parameter:

PowerShell Get-HotFix ^| Out-GridView -Wait

Powershell.exe -Command "Get-HotFix | Out-GridView -Wait"

Get-HotFix | Out-GridView -Wait

See:

In C#

If I ever want to do the same from C#, I need to figure out where to get the WUApiLib from; more on that library is at [WayBack] Use C# to interact with Windows Update – Stack Overflow and [WayBack] Searching, Downloading, and Installing Updates (Windows).

–jeroen

Posted in Development, Power User, PowerShell, Scripting, Software Development, Windows | Leave a Comment »

 
%d bloggers like this: