@@ -104,58 +104,37 @@ function Watch-FleetCPU
104104 $lines
105105 }
106106
107+ # minimum clip, the vertical height available for the cpu core bars
108+ $minClip = 10
109+
107110 # these are the valid divisions, in units of percentage width.
108111 # they must evenly divide 100% and either 10% or 20% for scale markings.
109112 # determine which is the best fit based on window width.
110113
111114 $div = 0
112- foreach ($i in 1 , 2 , 4 , 5 ) {
115+ $divs = 1 , 2 , 4 , 5
116+ foreach ($i in $divs ) {
113117 if ((div- to- width $i ) -le [console ]::WindowWidth) {
114118 $div = $i
115119 break
116120 }
117121 }
118122
119- # if nothing fit, widen to 4% divisions
120-
123+ # if nothing fit ... ask for minimum
124+ # in practice this is not possible, but we check anyway
121125 if ($div -eq 0 ) {
122- $div = 4
126+ Write-Error " Window width must be at least $ ( div- to- width $divs [-1 ]) columns"
127+ return
123128 }
124129
125130 $width = div- to- width $div
126131
127- # get the constant legend; use the remaining height for the vertical cpu core bars.
128- # note total height includes variable label line at bottom (instance + aggregagte)
129- if ($Guest ) {
130- $legend = get-legend " Percent Guest VCPU Utilization" $width $div
131- } else {
132- $legend = get-legend " Percent Host LP Utilization" $width $div
133- }
134-
135- $clip = [console ]::WindowHeight - $legend.Count - 1
136-
137- # insist on a clip no lower than 10
138-
139- if ($clip -lt 10 ) {
140- $clip = 10
141- }
142-
143- # set window and buffer size simultaneously so we don't have extra scrollbars
144- cls
145- [console ]::SetWindowSize($width , $clip + $legend.Count + 1 )
146- [console ]::BufferWidth = [console ]::WindowWidth
147- [console ]::BufferHeight = [console ]::WindowHeight
148-
149- # scale divisions at x%
150- # this should evenly divide 100%
151- $m = [array ]::CreateInstance([int ], $width )
152-
153132 # which processor counterset should we use?
154133 # pi is only the root partition if hv is active; when hv is active:
155134 # hvlp is the host physical processors
156135 # hvvp is the guest virtual processors
157136 # via ctrs, hv is active iff hvlp is present and has multiple instances
158- $cs = get-counter - ComputerName $ComputerName - ListSet ' Hyper-V Hypervisor Logical Processor' - ErrorAction SilentlyContinue
137+ $cs = Get-Counter - ComputerName $ComputerName - ListSet ' Hyper-V Hypervisor Logical Processor' - ErrorAction SilentlyContinue
159138 $hvactive = $null -ne $cs -and $cs.CounterSetType -eq [Diagnostics.PerformanceCounterCategoryType ]::MultiInstance
160139
161140 if ($Guest -and -not $hvactive ) {
@@ -166,75 +145,138 @@ function Watch-FleetCPU
166145 if ($hvactive ) {
167146 if ($Guest ) {
168147 $cpuset = " \Hyper-V Hypervisor Virtual Processor(*)\% Guest Run Time"
148+ $legend = get-legend " Percent Guest VCPU Utilization" $width $div
169149 } else {
170150 $cpuset = ' \Hyper-V Hypervisor Logical Processor(*)\% Total Run Time'
151+ $legend = get-legend " Percent Host LP Utilization" $width $div
171152 }
172153 } else {
173154 $cpuset = ' \Processor Information(*)\% Processor Time'
155+ $legend = get-legend " Percent Processor Utilization" $width $div
174156 }
175157
176158 # processor performance counter (turbo/speedstep)
159+ # this is used to normalize the total cpu utilization (can be > 100%)
177160 $ppset = ' \Processor Information(_Total)\% Processor Performance'
178161
162+ # account for the constant legend in the window height
163+ # use the remaining height for the vertical cpu core bars.
164+ $clip = [console ]::WindowHeight - ($legend.Count + 1 )
165+
166+ # insist on a minimum amount of space
167+ if ($clip -lt $minClip ) {
168+ $minWindowHeight = $minClip + $legend.Count + 1
169+ Write-Error " Window height must be at least $minWindowHeight lines"
170+ return
171+ }
172+
173+ # set window and buffer size simultaneously so we don't have extra scrollbars
174+ Clear-Host
175+ [console ]::SetWindowSize($width , [console ]::WindowHeight)
176+ [console ]::BufferWidth = [console ]::WindowWidth
177+ [console ]::BufferHeight = [console ]::WindowHeight
178+
179+ # common params for Get-Counter
180+ $gcParam = @ {
181+ SampleInterval = $SampleInterval
182+ Counter = $cpuset , $ppset
183+ ErrorAction = ' SilentlyContinue'
184+ }
185+
179186 while ($true ) {
180187
181- # reset measurements & the lines to output
182- $lines = @ ()
183- foreach ($i in 0 .. ($m.length - 1 )) {
184- $m [$i ] = 0
185- }
188+ # reset measurements
189+ # these are the vertical height of a cpu bar in each column, e.g. 2 = 2 cpus
190+ $m = @ ([int ] 0 ) * $width
186191
187192 # avoid remoting for the local case
188193 if ($ComputerName -eq $env: COMPUTERNAME ) {
189- $samp = ( get-counter - SampleInterval $SampleInterval - Counter $cpuset , $ppset ).Countersamples
194+ $ctrs = Get- Counter @gcParam
190195 } else {
191- $samp = ( get-counter - SampleInterval $SampleInterval - Counter $cpuset , $ppset - ComputerName $ComputerName ).Countersamples
196+ $ctrs = Get- Counter @gcParam - ComputerName $ComputerName
192197 }
193198
194- # get all specific instances and count them into appropriate measurement bucket
195- $samp | % {
199+ # if more than one countersample was returned (ppset + something more), we have data
200+ if ($null -ne $ctrs -and $ctrs.Countersamples.Count -gt 1 )
201+ {
202+ # get all specific instances and count them into appropriate measurement bucket
203+ $ctrs.Countersamples | % {
196204
197- if ($_.Path -like " *$ppset " ) { # scaling factor for total utility
198- $pperf = $_.CookedValue / 100
199- } elseif ($_.InstanceName -notlike ' *_Total' ) { # per cpu: ignore total and per-numa total
200- $m [[math ]::Floor ($_.CookedValue / $div )] += 1
201- } elseif ($_.InstanceName -eq ' _Total' ) { # get total
202- $total = $_.CookedValue
205+ # get scaling factor for total utility
206+ if ($_.Path -like " *$ppset " ) {
207+ $pperf = $_.CookedValue / 100
208+ }
209+
210+ # a cpu: count into appropriate measurement bucket
211+ # (ignore total and/or and per-numa total)
212+ elseif ($_.InstanceName -notlike ' *_Total' ) {
213+ $m [[math ]::Floor ($_.CookedValue / $div )] += 1
214+ }
215+
216+ # get total
217+ #
218+ elseif ($_.InstanceName -eq ' _Total' ) {
219+ $total = $_.CookedValue
220+ }
203221 }
204- }
205222
206- # work down the veritical altitude of each strip, starting at vclip
207- $altitude = $clip
208- do {
209- $lines += , ($ ($m | % {
223+ # now produce the bar area as a series of lines
224+ # work down the veritical altitude of each strip, starting at the clip/top
225+ $altitude = $clip
226+ $lines = do {
227+ $ ($m | % {
228+
229+ # top line - if we are potentially clipped, handle
230+ if ($altitude -eq $clip ) {
231+
232+ # clipped?
233+ if ($_ -gt $altitude ) { ' x' }
234+ # unclipped but at clip?
235+ elseif ($_ -eq $altitude ) { ' *' }
236+ # nothing, bar less than altitude
237+ else { ' ' }
238+
239+ } else {
240+
241+ # below top line
242+ # >=, output bar
243+ if ($_ -ge $altitude ) { ' *' }
244+ # <, nothing
245+ else { ' ' }
246+ }
247+ }) -join ' '
248+ } while (-- $altitude )
210249
211- # if we are potentially clipped, handle
212- if ( $altitude -eq $clip ) {
250+ $totalStr = " {0:0.0}% " -f $total
251+ $normalStr = " {0:0.0}% " -f ( $total * $pperf )
213252
214- # clipped?
215- # unclipped but at clip?
216- # nothing, less than altitude
253+ # move the cursor to indicate average utilization
254+ # column number is zero based, width is 1-based
255+ $cpos = [math ]::Floor(($width - 1 )* $total / 100 )
256+ }
257+ else
258+ {
259+ # Center no data message vertically and horizontally in the frame
217260
218- if ($_ -gt $altitude ) { ' x' }
219- elseif ($_ -eq $altitude ) { ' *' }
220- else { ' ' }
261+ $vpre = [math ]::Floor($clip / 2 ) - 1
262+ $vpost = [math ]::Floor($clip / 2 )
221263
222- } else {
223- # normal
224- # >=, output
225- # <, nothing
226- if ( $_ -ge $altitude ) { ' * ' }
227- else { ' ' }
228- }
229- }) -join ' ' )
230- } while ( -- $altitude )
264+ $lines = @ ( ' ' ) * $vpre
265+ $lines += center - pad " No Data Available " $width
266+ $lines += @ ( ' ' ) * $vpost
267+
268+ $totalStr = $normalStr = " --- "
269+
270+ # zero cursor
271+ $cpos = 0
272+ }
231273
232- cls
233- write-host - NoNewline ($lines + $legend -join " `n " )
234- write-host - NoNewLine (" `n " + (center- pad ( " {2} Total: {0:0.0}% Normalized: {1:0.0}% " -f $total , ( $total * $pperf ) , $ComputerName ) $width ))
274+ Clear-Host
275+ Write-Host - NoNewline ($lines + $legend -join " `n " )
276+ Write-Host - NoNewLine (" `n " + (center- pad " $ComputerName Total: $totalStr Normalized: $normalStr " $width ))
235277
236278 # move the cursor to indicate average utilization
237279 # column number is zero based, width is 1-based
238- [console ]::SetCursorPosition([ math ]::Floor(( $width - 1 ) * $total / 100 ) , [console ]::CursorTop- $legend.Count )
280+ [console ]::SetCursorPosition($cpos , [console ]::CursorTop- $legend.Count )
239281 }
240282}
0 commit comments