I have a Samurize config that shows a CPU usage graph similar to Task manager.
How do I also display the name of the process with the current highest CPU usage percentage?
I would like this to be updated, at most, once per second. Samurize can call a command line tool and display it's output on screen, so this could also be an option.
Further clarification:
I have investigated writing my own command line c# .NET application to enumerate the array returned from System.Diagnostics.Process.GetProcesses(), but the Process instance class does not seem to include a CPU percentage property.
Can I calculate this in some way?
What you want to get its the instant CPU usage (kind of)...
Actually, the instant CPU usage for a process does not exists. Instead you have to make two measurements and calculate the average CPU usage, the formula is quite simple:
AvgCpuUsed = [TotalCPUTime(process,time2) - TotalCPUTime(process,time1)] / [time2-time1]
The lower Time2 and Time1 difference is, the more "instant" your measurement will be. Windows Task Manager calculate the CPU use with an interval of one second. I've found that is more than enough and you might even consider doing it in 5 seconds intervals cause the act of measuring itself takes up CPU cycles...
So, first, to get the average CPU time
using System.Diagnostics;
float GetAverageCPULoad(int procID, DateTme from, DateTime, to)
{
// For the current process
//Process proc = Process.GetCurrentProcess();
// Or for any other process given its id
Process proc = Process.GetProcessById(procID);
System.TimeSpan lifeInterval = (to - from);
// Get the CPU use
float CPULoad = (proc.TotalProcessorTime.TotalMilliseconds / lifeInterval.TotalMilliseconds) * 100;
// You need to take the number of present cores into account
return CPULoad / System.Environment.ProcessorCount;
}
now, for the "instant" CPU load you'll need an specialized class:
class ProcLoad
{
// Last time you checked for a process
public Dictionary<int, DateTime> lastCheckedDict = new Dictionary<int, DateTime>();
public float GetCPULoad(int procID)
{
if (lastCheckedDict.ContainsKey(procID))
{
DateTime last = lastCheckedDict[procID];
lastCheckedDict[procID] = DateTime.Now;
return GetAverageCPULoad(procID, last, lastCheckedDict[procID]);
}
else
{
lastCheckedDict.Add(procID, DateTime.Now);
return 0;
}
}
}
You should call that class from a timer (or whatever interval method you like) for each process you want to monitor, if you want all the processes just use the Process.GetProcesses static method
Building on Frederic's answer and utilizing the code at the bottom of the page here (for an example of usage see this post) to join the full set of processes gotten from Get-Process, we get the following:
$sampleInterval = 3
$process1 = Get-Process |select Name,Id, #{Name="Sample1CPU"; Expression = {$_.CPU}}
Start-Sleep -Seconds $sampleInterval
$process2 = Get-Process | select Id, #{Name="Sample2CPU"; Expression = {$_.CPU}}
$samples = Join-Object -Left $process1 -Right $process2 -LeftProperties Name,Sample1CPU -RightProperties Sample2CPU -Where {$args[0].Id -eq $args[1].Id}
$samples | select Name,#{Name="CPU Usage";Expression = {($_.Sample2CPU-$_.Sample1CPU)/$sampleInterval * 100}} | sort -Property "CPU Usage" -Descending | select -First 10 | ft -AutoSize
Which gives an example output of
Name CPU Usage
---- ---------
firefox 20.8333333333333
powershell_ise 5.72916666666667
Battle.net 1.5625
Skype 1.5625
chrome 1.5625
chrome 1.04166666666667
chrome 1.04166666666667
chrome 1.04166666666667
chrome 1.04166666666667
LCore 1.04166666666667
You might be able to use Pmon.exe for this. You can get it as part of the Windows Resource Kit tools (the link is to the Server 2003 version, which can apparently be used in XP as well).
Process.TotalProcessorTime
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.totalprocessortime.aspx
Somehow
Get-Process | Sort-Object CPU -desc | Select-Object -first 3 | Format-Table CPU,ProcessName,TotalProcessorTime -hidetableheader
wasn't getting the CPU information from the remote machine. I had to come up with this.
Get-Counter '\Process(*)\% Processor Time' | Select-Object -ExpandProperty countersamples | Select-Object -Property instancename, cookedvalue| Sort-Object -Property cookedvalue -Descending| Select-Object -First 10| ft -AutoSize
Thanks for the formula, Jorge. I don't quite understand why you have to divide by the number of cores, but the numbers I get match the Task Manager. Here's my powershell code:
$procID = 4321
$time1 = Get-Date
$cpuTime1 = Get-Process -Id $procID | Select -Property CPU
Start-Sleep -s 2
$time2 = Get-Date
$cpuTime2 = Get-Process -Id $procID | Select -Property CPU
$avgCPUUtil = ($cpuTime2.CPU - $cpuTime1.CPU)/($time2-$time1).TotalSeconds *100 / [System.Environment]::ProcessorCount
You can also do it this way :-
public Process getProcessWithMaxCPUUsage()
{
const int delay = 500;
Process[] processes = Process.GetProcesses();
var counters = new List<PerformanceCounter>();
foreach (Process process in processes)
{
var counter = new PerformanceCounter("Process", "% Processor Time", process.ProcessName);
counter.NextValue();
counters.Add(counter);
}
System.Threading.Thread.Sleep(delay);
//You must wait(ms) to ensure that the current
//application process does not have MAX CPU
int mxproc = -1;
double mxcpu = double.MinValue, tmpcpu;
for (int ik = 0; ik < counters.Count; ik++)
{
tmpcpu = Math.Round(counters[ik].NextValue(), 1);
if (tmpcpu > mxcpu)
{
mxcpu = tmpcpu;
mxproc = ik;
}
}
return processes[mxproc];
}
Usage:-
static void Main()
{
Process mxp=getProcessWithMaxCPUUsage();
Console.WriteLine(mxp.ProcessName);
}
With PowerShell:
Get-Process | Sort-Object CPU -desc | Select-Object -first 3 | Format-Table CPU,ProcessName -hidetableheader
returns somewhat like:
16.8641632 System
12.548072 csrss
11.9892168 powershell
Related
I am using PowerShell to build some scripts in an Active Directory enviroment and am currently struggling to find a way to count objects. My base search is:
$DClist = (Get-ADForest).Domains | % { Get-ADDomainController –Filter * -Server $_ } | Select Site, Name, Domain
And it generates the following output:
Site Name Domain
---- ---- ------
Site-A DC-123 acme.local
Site-A DC-ABC acme.local
Site-B DC-XYZ domain.local
Site-C DC-YPT domain.local
Now I would like to count the number of objects in the column 'Name' and display something like this:
Site Count_of_Name
---- ----
Site-A 2
Site-B 1
Site-C 1
I have already tried a lot of things and the closest I got so far was using:
$DcList | Group-Object Site
But unfortunately it is not the right way to go as it only counts the number of 'Site' and "ignores" the rest. Also tried this, but it did not work as I expect either:
$DcList | Group-Object Site, Name
Please help me figure out the logic of this.
********************** UPDATE **********************
I have finally been able to come to this, but I cannot figure out a way to count the objects from 'Site' column:
$DClist | Group-Object -Property Site | ForEach-Object -Process {
[PSCustomObject]#{
Site = $_.Name
DCs = ($_.Group.Site)
}
}
Please help me out. I feel I'm so close to a solution now. :)
you are REALLY close, and the answer is about what you would expect :)
when doing group you automatically get a count property. just use this.
$DClist = (Get-ADForest).Domains | % { Get-ADDomainController –Filter * -Server $_ } | Select Site, Name, Domain
$dclist|Group-Object site|ForEach-Object{
[PSCustomObject]#{
site = $_.name
DCs = $_.group
count = $_.count
}
}
edit:
you could also do this that could be even faster if propegating through many objects. when doing select you can add a custom query and a label for that query.
#{name='fieldname';expression={$_.reference.to.object}} or #{n='field';e={$_.expression}} if you want to shorten it.
$dclist|Group-Object site|ForEach-Object{
$_|select #{n='site';e={$_.name}},count,#{n='DCs';e={$_.group}}
}
I don't exactly understand what you really want, but if it is some kind of tree, this will show it:
ForEach($Site in ($dclist | Group-Object site))
{
$Site.Count.ToString() + " " + $Site.Name
ForEach($Server in $Site.Group)
{
" + " + $Server.Name
}
}
Output:
2 Site-A
+ DC-123
+ DC-ABC
1 Site-B
+ DC-XYZ
1 Site-C
+ DC-YPT
If I look in Task Manager, I can see "In Use" memory.
I know that I can grab performance information in PerfMon, but I do not know which counter in Perfmon retrieves this value.
I want to write a PowerShell script to find out average memory usage for the past day. PerfMon is the only option that I can think of. Is there a better way to do this in PowerShell?
Get-Counter -Counter is the way to get performance counters in PowerShell 2+. "In use" looks like it's the rounded value of Total Memory - Available:
[math]::Round(((((Get-Ciminstance Win32_OperatingSystem).TotalVisibleMemorySize * 1kb) - ((Get-Counter -Counter "\Memory\Available Bytes").CounterSamples.CookedValue)) / 1GB),1)
What I usually do is run the following to get the current:
The $UsedRAM variable is what you are looking for.
$SystemInfo = Get-WmiObject -Class Win32_OperatingSystem | Select-Object Name, TotalVisibleMemorySize, FreePhysicalMemory
$TotalRAM = $SystemInfo.TotalVisibleMemorySize/1MB
$FreeRAM = $SystemInfo.FreePhysicalMemory/1MB
$UsedRAM = $TotalRAM - $FreeRAM
$RAMPercentFree = ($FreeRAM / $TotalRAM) * 100
$TotalRAM = [Math]::Round($TotalRAM, 2)
$FreeRAM = [Math]::Round($FreeRAM, 2)
$UsedRAM = [Math]::Round($UsedRAM, 2)
$RAMPercentFree = [Math]::Round($RAMPercentFree, 2)
Now we know how to get the current/In Use memory, but getting the average takes some more code.
Using the Get-Counter we can setup a counter of the average, but note that this will only provide the average during the time of testing and doesn't go back in time.
To get a good understanding of the average, I do about 1000 counts. Note this will also consume memory. Depending on the language of the system the formatting can be wrong.
$interval = 1 #seconds
$maxsamples = 1000
$memorycounter = (Get-Counter "\Memory\Available MBytes" -maxsamples $maxsamples -sampleinterval $interval |
select -expand countersamples | measure cookedvalue -average).average
### Memory Average Formatting ###
$freememavg = "{0:N0}" -f $memorycounter
### Get total Physical Memory & Calculate Percentage ###
$physicalmemory = (Get-WMIObject -class Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).Sum / 1mb
$physicalmemory - $memorycounter
#$physicalmemory - $freememavg #Depending on the Formatting of your system
This can also be done with the CPU and DISK.
I have job to get the following ESXi host information, while I am coding the powercli, I need to get the consumed CPU Mhz, but there is no field to get the value directly, so I custom a field and write some formula to caculate it, Called "Host CPU - Mhz". However, the following information will write into the excel file, I have some confuse how to get the custom field in the powercli
here is my part of code
$hostState = "" | get-vm | where-object {$_.VMHost -match '10.0.0.100'} | select Name, PowerState, #{N="Host CPU - MHz";E={[Math]::Round(($_ | get-stat -stat cpu.usagemhz.average -Start (Get-Date).AddDays(-1) -IntervalMins 1440 | Measure-object Value -Average).Average,2)}}
$hName = $hostState.Name
$hPowerState = $hostState.PowerState
$hCPU = << how to do it?>>
kindly advice and help.
$hCPU = $hostState."Host CPU - MHz"
I am trying to check memory types on all PCs across company. My testing code is below based on info from here:
Get-WmiObject Win32_PhysicalMemory |
Select-Object -Property PSComputerName, DeviceLocator, Manufacturer, PartNumber, #{label = "Size/GB" ; Expression = {$_.capacity / 1GB}}, Speed, datawidth, totalwidth, #{label = "ECC" ; Expression = {
if ( $_.totalwidth > $_.datawidth ) {
"$($_.DeviceLocator) is ECC memory type"
}
else {
"$($_.DeviceLocator) is non-ECC Memory Type"
}
}
} | Out-GridView
The results showing me that memory type is non-ecc:
But if I use 3rd party tool like "HWiNFO64 v4.30" the result is ECC memory. See pic below. How can I get the same memory info like pic below by using PowerShell? Speciously "Memory type" "Speed" and "ECC"
Vikas could have some good points about the accuracy of the information which should be considered. The linked post eludes to other issues as well.
The issue you are running into with this code is your use of PowerShell Comparison Operators.
They are in the format of -gt and -lt for example which are greater than and less than respectively. Assuming your logic you should just have to update
if ( $_.totalwidth > $_.datawidth )
to
if ( $_.totalwidth -gt $_.datawidth )
Below I have some code to get the values of instances of performance counters (which are instantiated once a page is visited) and send them to Graphite to display graphs in the following format:
[Path in Graphite (e.g., metric.pages.Counter1)] [value of counter] [epoch time]
To do this I made the following code where the writer is configured correctly to work:
# Get all paths to MultipleInstance counters and averages that start with "BLABLA" and
# put them into an array and get the epoch time
$pathsWithInstances = (get-counter -ListSet BLABLA*) | select -ExpandProperty PathsWithInstances
$epochtime = [int][double]::Parse((Get-Date -UFormat %s))
# This functions splits the path (e.g., \BLABLA Web(welcome)\Page Requests) into three
# parts: the part before the
# opening brace (the CounterCategory, e.g., "\BLABLA Web"), the part in between the braces
# (the page or
# service, e.g., "welcome"), and the part after the closing brace (the name of the test,
# e.g.,
# "\Page Requests"). We obtain the metric out of this information and send it to
# Graphite.
enter code here
foreach ($pathWithInstance in $pathsWithInstances)
{
$instanceProperties = $pathWithInstance.Split('()')
$counterCategory = $instanceProperties[0]
if ($counterCategory -eq ("\BLABLA Web") )
{
# Replace the * with nothing so that counters that are used to display the
# average (e.g., \BLABLAWeb(*)\Page Requests) are displayed on top in the
# Graphite directory.
$pagePath = $instanceProperties[1].Replace('*','')
$nameOfTheTest = $instanceProperties[2]
# Countername which is used in Graphite path gets whitespace and backslash
# removed in the name used for the path in Graphite (naming conventions)
$counterName = $nameOfTheTest.Replace(' ','').Replace('\','')
$pathToPerfCounter = $pathWithInstance
$pathInGraphite = "metrics.Pages." + $pagePath + $counterName
#Invoked like this since otherwise the get-counter [path] does not seem to work
$metricValue = [int] ((Get-Counter "$pathToPerfCounter").countersamples | select -
property cookedvalue).cookedvalue
$metric = ($pathInGraphite + " " + $metricValue + " " + $epochTime)
$writer.WriteLine($metric)
$writer.Flush()
}
}
Unfortunately this code is very slow. It takes about one second for every counter to send a value. Does someone see why it is so slow and how it can be improved?
You're getting one counter at a time, and it takes a second for Get-Counter to get and "Cook" the values. Get-Counter will accept an array of counters, and will sample, "cook" and return them all in that same second. You can speed it up by sampling them all at once, and then parsing the values from the array of results:
$CounterPaths = (
'\\Server1\Memory\Page Faults/sec',
'\\Server1\Memory\Available Bytes'
)
(Measure-Command {
foreach ($CounterPath in $CounterPaths)
{Get-Counter -counter $counterpath}
}).TotalMilliseconds
(Measure-Command {
Get-Counter $CounterPaths
}).TotalMilliseconds
2017.4693
1012.3012
Example:
foreach ($CounterSample in (Get-Counter $CounterPaths).Countersamples)
{
"Path = $($CounterSample.path)"
"Metric = $([int]$CounterSample.CookedValue)"
}
Path = \\Server1\memory\page faults/sec
Metric = 193
Path = \\Server1\memory\available bytes
Metric = 1603678208
Use the Start-Job cmdlet, to create separate threads for each counter.
Here is a simple example of how to take the Counter Paths and pass them into an asynchronous ScriptBlock:
$CounterPathList = (Get-Counter -ListSet Processor).PathsWithInstances.Where({ $PSItem -like '*% Processor Time' });
foreach ($CounterPath in $CounterPathList) {
Start-Job -ScriptBlock { (Get-Counter -Counter $args[0]).CounterSamples.CookedValue; } -ArgumentList $CounterPath;
}
# Call Receive-Job down here, once all jobs are finished
IMPORTANT: The above example uses PowerShell version 4.0's "method syntax" for filtering objects. Please make sure you're running PowerShell version 4.0, or change the Where method to use the traditional Where-Object instead.