Powershell Get a specific process counter with id process - powershell

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

Related

Apply conditional to output of a commandlet

I want to output the result of the commandlet (Invoke-Command) on success and add a custom message if the result is null. The code as shown below produces the desired results except in the event of a null response, it simply outputs nothing on that line.
I can not pipe directly to an if statement, nor can I output on 2 opposing conditions (True & False). Is it possible to get a custom response on $null while not suppressing the normal output on success?
Invoke-Command -ComputerName PC1, PC2, PC3 -Scriptblock {get-eventlog system | where-object {$_.eventid -eq 129} | select MachineName, EventID, TimeGenerated, Message -last 1}
If you run the example code block assuming that PC1 and PC3 have the event ID but PC2 does not, the output will simply skip PC2.
I want to output something like "Event Not found" in that case.
Placing the entire thing in a loop and then running the results through another conditional loops destroys performance so that is not an ideal solution.
I would create a new object for returning from Invoke-Command. So you are sure you will receive from every host something even the event log is not present. And might you can change get-eventlog to Get-WinEvent. Get-WinEvent was for my tasks the most time faster than get-eventlog.
[System.Management.Automation.ScriptBlock]$Scriptblock = {
[System.Collections.Hashtable]$Hashtable = #{
WinEvent = Get-WinEvent -FilterHashtable #{ LogName = 'System'; Id = 129 } -MaxEvents 1 -ErrorAction SilentlyContinue #-ErrorAction SilentlyContinue --> otherwise there is an error if no event is available
}
return (New-Object -TypeName PSCustomObject -Property $Hashtable)
}
Invoke-Command -ComputerName 'PC1', 'PC2', 'PC3' -Scriptblock $Scriptblock

Powershell Process CPU checking

Have the following which works OK, but with an in issue in PowerShell:
$FileName = "E:\Work\ps\Inventory.htm"
$serverlist = "E:\Work\ps\Monitored_computers.txt"
foreach ($server in Get-Content $serverlist)
{
$servern=$server.split(",")[0]
$ip=$server.split(",")[1]
$cpu = gwmi Win32_PerfFormattedData_PerfProc_Process -Computer $servern -filter "Name <> '_Total' and Name <> 'Idle'" | Sort-Object PercentProcessorTime -Descending | where { $_.PercentProcessorTime -gt 0 }| select -First 1
if ($cpu.PercentProcessorTime -ge "92") {
write-host $servern ' ' $cpu.Name ' ' $cpu.PercentProcessorTime
}
}
I have seen some other code in PowerShell, that takes an Average but almost seems like an "average of an average" - which is meaningless. And, this is for overall CPU Usage
gwmi win32_processor | Measure-Object -property LoadPercentage -Average | Foreach {$_.Average}
Now, if we can take the same logic and apply for our process issue:
gwmi Win32_PerfFormattedData_PerfProc_Process | Sort-Object PercentProcessorTime -Descending | where { $_.PercentProcessorTime -gt 0 } | select -First 1 | Measure-Object -property PercentProcessorTime -Average | Foreach {$_.PercentProcessorTime}
What am trying to ask is: I do get the CPU Percentage, which seems to be a "point in time". How do locate the true CPU Percentage? This is why I am pointing out the average. I really want to get around the "point in time" part of this.
The point being, when we have seen on several occasions, a high CPU per process on a server, we login to the server and the high CPU has subsided. This is not to say, this has been each time, but we know that sometimes a CPU will spike and then quiet down.
Thanks for any insight!
First issue, you are stuck at a Point In Time because when you execute your script it captures a snapshot of what is happening right then and there. What you are looking for is historical data, so you can figure out the average CPU usage of processes over a set amount of time, and pinpoint the process that's bogging down your CPU. Do you have performance monitors in place to track CPU usage for individual processes? You may need to setup performance logging if you want to be able to get the numbers you're looking for after the fact.
Secondly, I think that you misunderstand how Measure-Object works. If you run Get-Help on the cmdlet and check the Output you'll see that it outputs a GenericMeasureInfo object. This object will have a property for the relevant stat that you are looking for, which in your case is the Average property. It is not an average of an average, the most common usage I see for it is to calculate something, like a Sum or Average, and then output the value of that property.
Let's try a simple example...
Find the average size of the files in a folder. First we use Get-ChildItem to get a collection of files, and pipe it to Measure-Object. We will specify the -Average argument to specify that we want the Average calculated, and -Property length, so that it knows what to average:
GCI C:\Temp\* -file | Measure-Object -Average -Property length
This outputs a GenericMeasureInfo object like this:
Count : 30
Average : 55453155
Sum :
Maximum :
Minimum :
Property : Length
That lets me know that it had 30 files piped to it, and it found the Average for the Length property. Now, sometime you want to calculate more than one thing, so you can use more than one argument, such as -Sum and -Maximum, and those values will be populated as well:
Count : 30
Average : 55453155
Sum : 1663594650
Maximum : 965376000
Minimum :
Property : Length
So it looks like my average file is ~55MB, but out of the 1.6GB in the whole folder I've got one file that's 965MB! That file is undoubtedly skewing my numbers. With that output I could find folders that have multiple files, but one file is taking up over half of the space for the folder, and find anomalies... such as the ISO that I have saved to my C:\temp folder for some reason. Looks like I need to do some file maintenance.
Thanks to #TheMadTechnician I have been able to sort this out. I had a wrong component with
$_.Average
where I had
$_.PercentProcessorTime
and that would never work. Here is the correct script:
$serverlist = "D:\Work\ps\Monitored_computers.txt"
foreach ($server in Get-Content $serverlist) {
$servern=$server.split(",")[0]
$ip=$server.split(",")[1]
$cpu = gwmi Win32_PerfFormattedData_PerfProc_Process -Computer $ip | `
Where-Object {$_.Name -like "*tomcat*"} | `
Measure-Object -property PercentProcessorTime -Average | `
Foreach {$_.Average}
if ($cpu -ge "20") {
write-host $servern $cpu ' has a tomcat process greater than 20'
}
}

Powershell get process name (with #) by PID when multiple instances

I am trying to use Get-Counter '\process(APPNAME)\% processor time however for many processes APPNAME repeats. I can use APPNAME#1, APPNAME#2, APPNAME#3 in Get-Counter '\process(APPNAME#2)\% processor time. However, I can't find how to get the "full" appname (i.e the one that has # in it) from just the PID, is this possible?
The second answer here seems to explain it, but I don't understand exactly what they are doing.
Difficult to help you on this accurately because counters are culture sensitive and I'am too lazy to start my US VM.
You can find the PID of a process using :
Get-Counter '\\ComputerName\processus(chrome#4)\id de processus'
For me it gives :
Timestamp CounterSamples
--------- --------------
29/10/2015 08:31:55 \\ComputerName\processus(chrome#4)\id de processus :
3296
For UK or US I would try :
Get-Counter '\\ComputerName\process(chrome#4)\process id'
This One Line (that you have to adapt to your culture) gives a process list with PIDs and counter instance Id.
(Get-Counter '\processus(*)\id de processus').CounterSamples | % {[regex]$a = "^.*\($([regex]::Escape($_.InstanceName))(.*)\).*$";[PSCustomObject]#{InstanceName=$_.InstanceName;PID=$_.CookedValue;InstanceId=$a.Matches($($_.Path)).groups[1].value}}
Edited :
So here is a solution with the PID in input :
$p = $((Get-Counter '\processus(*)\id de processus' -ErrorAction SilentlyContinue).CounterSamples | % {[regex]$a = "^.*\($([regex]::Escape($_.InstanceName))(.*)\).*$";[PSCustomObject]#{InstanceName=$_.InstanceName;PID=$_.CookedValue;InstanceId=$a.Matches($($_.Path)).groups[1].value}})
$id = # your process id
$p1 = $p | where {$_.PID -eq $id}
Get-Counter -Counter "\Process($($p1.InstanceName+$p1.InstanceId))\% Processor Time"
# In french
# Get-Counter -Counter "\Processus($($p1.InstanceName+$p1.InstanceId))\% temps processeur"
you need something like this, select the items you need, and after that join them together.
Get-Process | Select ProcessName,Id | % {$_.ProcessName + " " + $_.Id}

Powershell: How can I extract time from the message field of eventlog?

I'm trying to get unexpected shutdown times of Windows Sever 2008 machines via Get-EventLog in Powershell. I can get close by searching for events with an EventID of 6008 and selecting only message, but I need to parse within the field to grab the time it occurred (not the time the event fired).
I've tried to use replacementstrings[x] but I can't find how to specify the field to use (messages) and can't get a result.
get-eventlog -LogName System -ComputerName svr-name | Where-Object {$_.EventID -eq 6008 -AND $_.timegenerated -gt (get-date).adddays(-30)}| select message
Produces this:
Message
-------
The previous system shutdown at 3:35:32 AM on ‎7/‎29/‎2014 was unexpected.
The previous system shutdown at 3:40:06 PM on ‎7/‎10/‎2014 was unexpected.`
Retrieving all events from a remote host and filtering them on the local machine ususally doesn't perform too well, because that way you transmit tons of unrelated events over the network, just to throw them away. Get-EventLog has options for filtering messages by Event ID or before/after a given timestamp on the source, so better use those for pre-selecting the messages you're actually interested in. The timestamp of the crash can be extracted from the Message field with a regular expression and parsed into a DateTime value via ParseExact():
$log = 'System'
$server = 'svr-name'
$id = [uint64]"0x80000000" + 6008
$date = (Get-Date).AddDays(-30)
$fmt = 'h:mm:ss tt on M\/d\/yyyy'
$culture = [Globalization.CultureInfo]::InvariantCulture
Get-EventLog -LogName $log -ComputerName $server -InstanceId $id -After $date | ? {
$_.Message -match 'at (\d+:\d+:\d+ [ap]m on \d+/\d+/\d+) was unexpected'
} | select MachineName, TimeGenerated,
#{n='Crashtime';e={[DateTime]::ParseExact($matches[1], $fmt, $culture)}}
The pipeline produces a list of objects with the properties MachineName, TimeGenerated and Crashtime (the last one being a calculated property). If you collect the output of the pipeline in a variable (e.g. $evt) you can access the Crashtime property of the third object like this:
$evt = .\script.ps1
$evt[2].Crashtime
Using regex, you can pull it out as such.
$Messages = (get-eventlog -LogName System -ComputerName svr-name | Where-Object {$_.EventID -eq 6008 -AND $_.timegenerated -gt (get-date).adddays(-30) }| select message)
$Messages | ForEach-Object {
$Matched = $_.Message -match "([0-9]{1,2}:.*[0-9]{4})"
if ($Matched) {
Write-Output "System rebooted at $($Matches[1])"
}
}
There might be a better way, but I do not know what :)
Example Output from my System
System rebooted at 4:34:30 PM on ‎4/‎20/‎2014
System rebooted at 1:48:38 PM on ‎1/‎21/‎2014
System rebooted at 1:37:12 PM on ‎1/‎21/‎2014
System rebooted at 1:22:01 PM on ‎1/‎21/‎2014
System rebooted at 4:41:21 PM on ‎11/‎22/‎2013
More easy
get-eventlog system | where-object {$_.EventID -eq "6008"} | fl

Retrieve Performance Counter's "Explain Text"

How can I get a Performance counter's Explain Text's string value thru Powershell.
I thought it would be a property of a counter
Get-Counter -Counter "\Processor(_Total)\% Processor Time"|gm
(Get-Counter '\logicalDisk(*)\Avg. Disk Queue Length').countersamples|gm
But it isn't. I found Lodctr /q to query the counters and this. However, I can't find exactly how to get the string value.
If you are ok with calling .net framework objects you have access to all the methods provided by PerformanceCounterCategory.
The following should help you get started:
$categoryName = "Processor"
$categoryInstance = "_Total"
$counterName = "% Processor Time"
# Call the static method to get the Help for the category
$categoryHelp = [System.Diagnostics.PerformanceCounterCategory]::GetCategories() | ?{$_.CategoryName -like $categoryName} | select -expandproperty CategoryHelp
# Create an instance so that GetCounters() can be called
$pcc = new-object System.Diagnostics.PerformanceCounterCategory($categoryName)
$counterHelp = $pcc.GetCounters($categoryInstance) | ?{$_.CounterName -like $counterName} | select -expandproperty CounterHelp
$categoryHelp
$counterHelp