Skip to content

Commit e99f442

Browse files
author
Dan Lovinger
committed
push to module version 2.1.0.0
update watch-fleetcpu to handle counter glitch / guest cpu non-availability
1 parent 8dfd24f commit e99f442

File tree

2 files changed

+115
-73
lines changed

2 files changed

+115
-73
lines changed

Frameworks/VMFleet/VMFleet.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ SOFTWARE.
3737
RootModule = 'VMFleet.psm1'
3838

3939
# Version number of this module. Even build# is release, odd pre-release (Mj.Mn.Bd.Rv)
40-
ModuleVersion = '2.0.2.3'
40+
ModuleVersion = '2.1.0.0'
4141

4242
# Supported PSEditions
4343
# CompatiblePSEditions = @()

Frameworks/VMFleet/WatchCPU.psm1

Lines changed: 114 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)