@@ -104,58 +104,37 @@ function Watch-FleetCPU
104
104
$lines
105
105
}
106
106
107
+ # minimum clip, the vertical height available for the cpu core bars
108
+ $minClip = 10
109
+
107
110
# these are the valid divisions, in units of percentage width.
108
111
# they must evenly divide 100% and either 10% or 20% for scale markings.
109
112
# determine which is the best fit based on window width.
110
113
111
114
$div = 0
112
- foreach ($i in 1 , 2 , 4 , 5 ) {
115
+ $divs = 1 , 2 , 4 , 5
116
+ foreach ($i in $divs ) {
113
117
if ((div- to- width $i ) -le [console ]::WindowWidth) {
114
118
$div = $i
115
119
break
116
120
}
117
121
}
118
122
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
121
125
if ($div -eq 0 ) {
122
- $div = 4
126
+ Write-Error " Window width must be at least $ ( div- to- width $divs [-1 ]) columns"
127
+ return
123
128
}
124
129
125
130
$width = div- to- width $div
126
131
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
-
153
132
# which processor counterset should we use?
154
133
# pi is only the root partition if hv is active; when hv is active:
155
134
# hvlp is the host physical processors
156
135
# hvvp is the guest virtual processors
157
136
# 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
159
138
$hvactive = $null -ne $cs -and $cs.CounterSetType -eq [Diagnostics.PerformanceCounterCategoryType ]::MultiInstance
160
139
161
140
if ($Guest -and -not $hvactive ) {
@@ -166,75 +145,138 @@ function Watch-FleetCPU
166
145
if ($hvactive ) {
167
146
if ($Guest ) {
168
147
$cpuset = " \Hyper-V Hypervisor Virtual Processor(*)\% Guest Run Time"
148
+ $legend = get-legend " Percent Guest VCPU Utilization" $width $div
169
149
} else {
170
150
$cpuset = ' \Hyper-V Hypervisor Logical Processor(*)\% Total Run Time'
151
+ $legend = get-legend " Percent Host LP Utilization" $width $div
171
152
}
172
153
} else {
173
154
$cpuset = ' \Processor Information(*)\% Processor Time'
155
+ $legend = get-legend " Percent Processor Utilization" $width $div
174
156
}
175
157
176
158
# processor performance counter (turbo/speedstep)
159
+ # this is used to normalize the total cpu utilization (can be > 100%)
177
160
$ppset = ' \Processor Information(_Total)\% Processor Performance'
178
161
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
+
179
186
while ($true ) {
180
187
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
186
191
187
192
# avoid remoting for the local case
188
193
if ($ComputerName -eq $env: COMPUTERNAME ) {
189
- $samp = ( get-counter - SampleInterval $SampleInterval - Counter $cpuset , $ppset ).Countersamples
194
+ $ctrs = Get- Counter @gcParam
190
195
} else {
191
- $samp = ( get-counter - SampleInterval $SampleInterval - Counter $cpuset , $ppset - ComputerName $ComputerName ).Countersamples
196
+ $ctrs = Get- Counter @gcParam - ComputerName $ComputerName
192
197
}
193
198
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 | % {
196
204
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
+ }
203
221
}
204
- }
205
222
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 )
210
249
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 )
213
252
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
217
260
218
- if ($_ -gt $altitude ) { ' x' }
219
- elseif ($_ -eq $altitude ) { ' *' }
220
- else { ' ' }
261
+ $vpre = [math ]::Floor($clip / 2 ) - 1
262
+ $vpost = [math ]::Floor($clip / 2 )
221
263
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
+ }
231
273
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 ))
235
277
236
278
# move the cursor to indicate average utilization
237
279
# 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 )
239
281
}
240
282
}
0 commit comments