This script is giving me the CPU for each node process. Which is great. But I need to know the PID for each process so that I can match specific process to CPU usage.
$ProcessName = "node"
$CpuCores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors
$Samples = (Get-Counter "\Process($Processname*)\% Processor Time").CounterSamples
$Samples | Select InstanceName,#{Name="CPU %";Expression={[Decimal]::Round(($_.CookedValue / $CpuCores), 4)}}
I've searched everywhere I can think. I've tried PID, ID, ProcessID, InstanceID and many other variants.
Seems like it should be simple?
You are dot referencing specific properties and that's all you'll get back. Thus, you cannot ask for what is not supplied.
# Using variable squeezing to output results to the screen while assigning results to the variable.
($ProcessName = "dllhost")
($CpuCores = (Get-WMIObject Win32_ComputerSystem).NumberOfLogicalProcessors)
($Samples = (Get-Counter "\Process($Processname*)\% Processor Time").CounterSamples)
($Samples | Select InstanceName,#{Name="CPU %";Expression={[Decimal]::Round(($_.CookedValue / $CpuCores), 4)}})
# Results
<#
dllhost
8
Path InstanceName CookedValue
---- ------------ -----------
\\lab01\process(dllhost#2)\% processor time dllhost 0
\\lab01\process(dllhost#1)\% processor time dllhost 0
\\lab01\process(dllhost)\% processor time dllhost 0
dllhost
dllhost
dllhost
#>
Get-Process -Name 'dllhost'
# Results
<#
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
120 7 1580 6760 0.11 8612 2 dllhost
200 17 3620 11332 0.09 14176 0 dllhost
229 16 3984 12216 0.36 15940 2 dllhost
#>
Get-Counter -Counter "\Process($Processname*)\% Processor Time"
# Results
<#
Timestamp CounterSamples
--------- --------------
23-Nov-19 17:52:10 \\lab01\process(dllhost#2)\% processor time :
3.11758575755139
\\lab01\process(dllhost#1)\% processor time :
0
\\lab01\process(dllhost)\% processor time :
0
#>
So, you need to combine the above for a single result. There are differ ways to do this of course, For example:
Clear-Host
Get-Process -Name 'WUDFHost' |
ForEach {
[PSCustomObject]#{
'ProcessName' = $PSItem.ProcessName
'ProcessId' = $PSItem.Id
'Path' = $PSItem.Path
'Cookedvalue' = ((Get-Counter -Counter "\Process($($PSItem.Name))\% Processor Time").CounterSamples).CookedValue
}
}
# Results
<#
ProcessName ProcessId Path Cookedvalue
----------- --------- ---- -----------
WUDFHost 1100 C:\Windows\System32\WUDFHost.exe 21.8216373679803
WUDFHost 4020 C:\Windows\System32\WUDFHost.exe 6.23866621508705
WUDFHost 4644 C:\Windows\System32\WUDFHost.exe 9.36077443109706
WUDFHost 10280 C:\Windows\System32\WUDFHost.exe 3.11837874640775
#>
Related
I am trying to create a script that gather the CPU and RAM information from the Local computer, but I need it to display in the same row.
$installed =
$processor = Get-WmiObject -Class Win32_Processor | Select-Object -Property Name
$memory = Get-WmiObject Win32_PhysicalMemory | Format-Table BankLabel, Capacity, Manufacturer
$result = $processor, $memory
$result | out-file test.txt
What I got
SystemName Name
---------- ----
EX Intel(R) Core(TM) i5-8500T CPU # 2.10GHz
Capacity Manufacturer
-------- ------------
8589934592 80AD000080AD
What I want to achieve
SystemName Name Capacity Manufacturer
---------- ---- ---------------------
EX Intel(R) Core(TM) i5-8500T CPU # 2.10GHz
Are there any ways to emerge the two tables?
Yes. Create a custom PowerShell object.
# Gather the data from the local (or remote) system
$processor = Get-WmiObject -Class Win32_Processor
$memory = Get-WmiObject Win32_PhysicalMemory
# Create a custom PowerShell object with the desired properties
[PSCustomObject]#{
SystemName = $processor.SystemName
Name = $processor.Name
MemoryCapacity = $memory.Capacity
Manufacturer = $memory.Manufacturer
}
Here's what the output looks like on my system.
SystemName Name MemoryCapacity Manufacturer
---------- ---- -------------- ------------
ARTEMIS AMD Ryzen 9 3900X 12-Core Processor {17179869184, 17179869184} {Unknown, Unknown}
I've created the following function for use cleaning up all references to com objects at the end of a script:
function TrashCompactor ($reflist) {
foreach ($ref in $Reflist){
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) | out-null
[Runtime.InteropServices.Marshal]::FinalReleaseComObject($ref) | out-null
Remove-Variable $ref | out-null
}
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
Will Remove-variable work as I expected? Is there any harm to including [System.GC]::Collect()?
Yes, and no... as this...
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
... is a common and best practice.
Windows will always do a cleanup, but it's always clean up your environment when you are done.
As documented...
Clean Up Your PowerShell Environment by Tracking Variable Use
https://devblogs.microsoft.com/scripting/clean-up-your-powershell-environment-by-tracking-variable-use
And covered by this SO Q&A and accepted answer...
PowerShell release COM object
function Release-Ref ($ref) {
[System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$ref) | out-null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
because I've noted that my comobject always stay alive, I think Powershell 2.0 is not able to remove comobject no more used.
[System.Runtime.InteropServices.Marshal]::ReleaseComObject( $ref )
and that SO is exactly what you are asking, so this question is really a duplicate.
My example, I use a prefix to my variable so they are easy to find and simple globally clean up.
# Assign results to a variable and output to the screen using variable squeezing
($ponMyShell = New-Object -com "Wscript.Shell")
($ponDate = Get-Date)
($ponProcess = Get-Process |
Select -First 3)
<#
# Results
Monday, 2 March, 2020 19:40:47
Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
------- ------ ----- ----- ------ -- -- -----------
186 14 2648 6800 0.14 15336 0 aesm_service
465 27 24300 34064 0.33 27612 22 ApplicationFrameHost
158 8 1928 4848 0.02 14268 0 AppVShNotify
SpecialFolders CurrentDirectory
-------------- ----------------
System.__ComObject C:\Windows\system32
#>
Get-Variable -Name 'pon*'
<#
# Results
Name Value
---- -----
ponDate 02-Mar-20 19:46:59
ponMyShell System.__ComObject
ponProcess {System.Diagnostics.Process (aesm_service), System.Diagnostics.Process (ApplicationFrameHost), System.Diagnostics.Process (AppVShNotify)}
#>
# Clear resource environment
Get-PSSession |
Remove-PSSession
<#
# Results
#>
[System.Runtime.InteropServices.Marshal]::
ReleaseComObject([System.__ComObject]$ponMyShell) |
Out-Null
<#
# Results
#>
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
<#
# Results
#>
Get-Variable -Name 'pon*' |
ForEach { Get-Variable -Name $_ |
Remove-Variable -Force }
# Validate clean-up
Get-Variable -Name 'pon*'
<#
# Results
#>
So I wrote a script to go through and optimize some VHDX files and output the results. In an effort to reduce the script run time (dramatically), I taught myself Workflow. That was an adventure on it's own but I got the script working up to the point of outputting the results. Calling the variable and piping it into a sort-object by percentage saved, then piped into a sort-object by job status, then piped into a format table; the code isn't actually sorting the data. It just outputs in order added to the array.
The code worked fine when it wasn't pulling the data from the Workflow (ran serially through the script, instead). My assumption is that the Workflow is outputting the data as a full table to the script array, rather than individual entries, so there isn't anything to sort by the time it gets to the script (the script sees it as a single entry).
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$listofprof = New-Object System.Collections.ArrayList
$UPDList = Get-ChildItem $PSScriptRoot -Filter *.vhdx | select -ExpandProperty Name
$updhome = $PSScriptRoot
Workflow TestCompact {
param(
[string]$updhome,
[array]$UPDList
)
Foreach -Parallel ($i in $UPDList) {
$listofproftemp =
InlineScript {
$startsize = ("{0:N2}" -f ((Get-ChildItem "$Using:updhome\$Using:i" | select -ExpandProperty length)/1GB))
$usersid = $Using:i -ireplace 'UVHD-|\.vhdx', ""
$username = (Get-ADUser -Filter 'SID -like $usersid' | Select-Object -ExpandProperty Name)
Try {
Optimize-VHD "$Using:updhome\$Using:i" -mode full -ErrorAction Stop
$jobstatus = "Completed"
} Catch [Microsoft.HyperV.PowerShell.VirtualizationException] {
$jobstatus = "Failed (UPD likely in use!)"
} Catch {
$jobstatus = "Failed (Something strange happened :( ....)"
}
$endsize = ("{0:N2}" -f ((Get-ChildItem "$Using:updhome\$Using:i" | select -ExpandProperty length)/1GB))
$percentagediff = ("{0:N2}" -f ((($startsize-$endsize)/$startsize)*100))
$temp = [PSCustomObject]#{
UserName = $username
StartSize = $startsize
EndSize = $endsize
PercentageSaved = $percentagediff
JobStatus = $jobstatus
FileName = $Using:i
}
$temp
}
$listofproftemp
}
}
$listofprof.add((TestCompact -updhome $updhome -updlist $UPDList)) | Out-Null
$listofprof | Sort-Object PercentageSaved | Sort-Object JobStatus | Format-Table -AutoSize -GroupBy JobStatus -Property UserName, StartSize, EndSize, PercentageSaved, JobStatus, FileName
$listofprof | Select-Object UserName, StartSize, EndSize, PercentageSaved, JobStatus, FileName | Export-Csv -Path "$updhome\$(get-date -Format yyyy-MM-dd-hhmmtt).csv"
I'm still working on optimizing and cleaning up the code but I'm trying to get it working first. Sorry in advance, it might be a bit difficult to read.
I anticipated getting output, to the console, of the object; sorted and grouped by status so the table only shows two or three sections. I also should have received an unsorted CSV of the data from the array but instead got an empty CSV with a row stating "#TYPE Selected.System.Object[]" and a row with the column names. The console output looks like the following:
JobStatus: Failed (UPD likely in use!)
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
C**** S**** 1.04 1.04 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
C***** S***** 1.07 1.07 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
JobStatus: Completed
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
A***** N**** M**** 0.50 0.50 0.00 Completed UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
JobStatus: Failed (UPD likely in use!)
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
M**** K***** 1.10 1.10 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
JobStatus: Completed
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
S******* G********** 1.82 1.82 0.00 Completed UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
The output should have looked like:
JobStatus: Completed
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
A***** N**** M**** 0.50 0.50 0.00 Completed UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
S******* G********** 1.82 1.82 0.00 Completed UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
JobStatus: Failed (UPD likely in use!)
UserName StartSize EndSize PercentageSaved JobStatus FileName
-------- --------- ------- --------------- --------- --------
M**** K***** 1.10 1.10 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
C**** S**** 1.04 1.04 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
C***** S***** 1.07 1.07 0.00 Failed (UPD likely in use!) UVHD-S-1-5-21-2318372095-3838506165-4286****09-5***.vhdx
Ref. http://powershell.com/cs/blogs/tips/archive/2013/04/16/documenting-cpu-load-for-running-processes.aspx
This PowerShell code runs great locally, but when changing it to remote computer, it does not work.. I would also think it be helpful to add not only CPU Usage, but memory as well.
$CPUPercent = #{
Name = 'CPUPercent'
Expression = {
$TotalSec = (New-TimeSpan -Start $_.StartTime).TotalSeconds
[Math]::Round( ($_.CPU * 100 / $TotalSec), 2)
}
}
Get-Process -computername RemoteServer |
Select-Object -Property Name, CPU, $CPUPercent, Description |
Sort-Object -Property CPUPercent -Descending |
Select-Object -First 4
And, shows for the results:
Name CPU
CPUPercent Description
---- --- ---------- ----------- svchost
sysedge
svchost
svchost
Changing it back to local run, we see:
Name
CPU CPUPercent Description
---- --- ---------- -----------
powershell_ise 5.578125 0.51 Windows PowerShell ISE
Application 2634.765625 0.24 Some Agent Executable
svchost 1926.96875 0.18 Generic Host Process for Win32 Services
mcshield 1903.875 0.17 McAfee On-Access Scanner service
I was wondering if it was possible to call a parameter twice within a cmdlet, such as:
cmdlet-test -myCommand input1, input2 -myCommand input3, input 4
myCommand being the same command twice. Is that possible or will a user just have to write it all in one comma separated list?
If the user attempts to use the same parameter twice, they will receive an error.
get-process | select -first 2
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
201 16 3324 3456 109 0.17 9972 acrotray
409 16 3904 4948 101 0.97 10520 AdobeARM
Let's try it the conventional way:
get-process -pid 9972,10520
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
201 16 3324 3456 109 0.17 9972 acrotray
409 16 3904 4948 101 0.97 10520 AdobeARM
And your proposed method:
get-process -pid 9972 -pid 10520
Get-Process : Cannot bind parameter because parameter 'Id' is specified more than once. To provide multiple values to
parameters that can accept multiple values, use the array syntax. For example, "-parameter value1,value2,value3".
At line:1 char:23
+ get-process -pid 9972 -pid 10520
+ ~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Process], ParameterBindingException
+ FullyQualifiedErrorId : ParameterAlreadyBound,Microsoft.PowerShell.Commands.GetProcessCommand
For those who would like to separate a really long function argument into several lines, I suggest this approach:
# Just an example function
function PrintFileName {
param (
[Parameter(Mandatory = $true)] [System.String[]] $Files = #()
)
foreach ($File in $Files) {
Write-Host "${File}"
}
}
Then, the function call like this:
PrintFileName -Files C:\SomeDirectory\SomeFile.txt1,C:\SomeDirectory\SomeFile2.txt,`
C:\SomeDirectory\SomeFile2.txt,C:\SomeDirectory\SomeFile3.txt,`
C:\SomeDirectory\SomeFile4.txt,C:\SomeDirectory\SomeFile5.txt
Will output:
C:\SomeDirectory\SomeFile1.txt
C:\SomeDirectory\SomeFile2.txt
C:\SomeDirectory\SomeFile2.txt
C:\SomeDirectory\SomeFile3.txt
C:\SomeDirectory\SomeFile4.txt
C:\SomeDirectory\SomeFile5.txt