ns::stats::Collector method cpuCapacity (public)

 <instance of ns::stats::Collector[i]> cpuCapacity

Defined in

Inspect host logical CPUs, process affinity and visible cgroup v1/v2 CPU quotas, cpusets and throttling counters. effectiveCores is the minimum known ceiling, not reserved or guaranteed CPU capacity. Missing probes remain unknown and are reflected in constraint completeness and reasons. Counters can include other processes or sibling cgroups; do not sum ancestor counters as independent process usage. Does not change limits.

Returns:
Dictionary of visible CPU ceilings, effectiveCores, constraint completeness, cumulative cgroup throttling counters and limitations; unavailable values are empty.

Testcases:
No testcase defined.
Source code:
set result {hostLogicalCpus {} affinityCpus {} cpusetCpus {} quotaCores {} effectiveCores {} quotaStatus unknown constraintsComplete false throttlingAvailable false throttlingScope cgroup-hierarchy throttling {} limitations {Visible CPU ceilings are not reserved capacity. Hidden ancestor limits, host contention and CPU shares can reduce availability. Throttling counters are cumulative for each visible cgroup, including other processes; ancestor counters can include sibling groups.}}
set reasons {}; set ceilings {}
if {![catch {:cpuListCount [:systemText /sys/devices/system/cpu/online]} count] && $count>0} {
    dict set result hostLogicalCpus $count; lappend ceilings $count
}
if {![catch {:systemText /proc/self/status} status]
    && [regexp -line {^Cpus_allowed_list:\s*([^\n]+)} $status -> list]
    && ![catch {:cpuListCount [string trim $list]} count] && $count>0} {
    dict set result affinityCpus $count; lappend ceilings $count
} else {lappend reasons affinity-unavailable}
set memberships {}
if {![catch {:systemText /proc/self/cgroup} text]} {
    foreach line [split $text \n] {
        if {![regexp {^[0-9]+:([^:]*):(.*)$} $line -> controllers path]} continue
        if {$controllers eq ""} {dict set memberships unified $path}
        foreach controller [split $controllers ,] {dict set memberships $controller $path}
    }
}
set quotaSeen false; set quotaMissing false; set hierarchySeen false
set groups {}; set visited {}
if {![catch {:systemText /proc/self/mountinfo} mounts]} {
    foreach line [split $mounts \n] {
        set parts [split $line " "]
        set separator [lsearch -exact $parts -]
        if {$separator<6} continue
        set type [lindex $parts [expr {$separator+1}]]
        if {$type ni {cgroup cgroup2}} continue
        set root [:mountPath [lindex $parts 3]]
        set mount [:mountPath [lindex $parts 4]]
        set controllers [split [lindex $parts [expr {$separator+3}]] ,]
        set key {}; set cpu false; set cpuset false
        if {$type eq "cgroup2" && [dict exists $memberships unified]} {
            set key unified; set cpu true; set cpuset true
        } else {
            foreach controller {cpu cpuset} {
                if {$controller in $controllers && [dict exists $memberships $controller]} {
                    set key $controller
                    if {$controller eq "cpu"} {set cpu true} else {set cpuset true}
                }
            }
        }
        if {$key eq ""} continue
        set member [dict get $memberships $key]
        # Do not traverse outside a visible mount, including namespace
        # membership paths containing parent components.
        if {".." in [file split $member]} {lappend reasons cgroup-path-unresolvable; continue}
        if {$root eq "/"} {set relative [string trimleft $member /]
        } elseif {$member eq "/" || $member eq $root} {set relative {}
        } elseif {[string first "$root/" $member]==0} {set relative [string range $member [expr {[string length $root]+1}] end]
        } else {continue}
        set directory [file join $mount $relative]
        set hierarchySeen true
        while {1} {
            if {[lsearch -exact $visited [list $directory $cpu $cpuset]]<0} {
                lappend visited [list $directory $cpu $cpuset]
                if {$cpu} {
                    if {$type eq "cgroup2"} {
                        set readable [expr {![catch {:systemText $directory/cpu.max} maximum]}]
                        if {$readable && [regexp {^(max|[0-9]+) ([1-9][0-9]*)$} $maximum -> quota period]} {
                            set quotaSeen true
                            if {$quota ne "max" && $quota>0} {lappend ceilings [expr {double($quota)/$period}]; set q [expr {double($quota)/$period}]
                                if {[dict get $result quotaCores] eq "" || $q<[dict get $result quotaCores]} {dict set result quotaCores $q}}
                        } elseif {$directory ne $mount} {set quotaMissing true}
                    } else {
                        if {![catch {:systemText $directory/cpu.cfs_quota_us} quota]
                            && ![catch {:systemText $directory/cpu.cfs_period_us} period]
                            && [string is entier -strict $quota] && [string is entier -strict $period] && $period>0} {
                            set quotaSeen true
                            if {$quota>0} {set q [expr {double($quota)/$period}]; lappend ceilings $q
                                if {[dict get $result quotaCores] eq "" || $q<[dict get $result quotaCores]} {dict set result quotaCores $q}}
                        } else {set quotaMissing true}
                    }
                    if {![catch {:systemText $directory/cpu.stat} counters]} {
                        set row [dict create path $directory]
                        foreach counter [split $counters \n] {
                            lassign $counter name value
                            if {![string is entier -strict $value] || $value<0} continue
                            switch -- $name {
                                nr_periods {dict set row periods $value}
                                nr_throttled {dict set row throttledPeriods $value}
                                throttled_usec {dict set row throttledSeconds [expr {$value/1000000.0}]}
                                throttled_time {dict set row throttledSeconds [expr {$value/1000000000.0}]}
                            }
                        }
                        if {[dict size $row]>1} {dict set groups $directory $row}
                    }
                }
                if {$cpuset} {
                    set file [expr {$type eq "cgroup2" ? "cpuset.cpus.effective" : "cpuset.cpus"}]
                    if {![catch {:systemText $directory/$file} list] && $list ne "" && ![catch {:cpuListCount $list} count] && $count>0} {
                        lappend ceilings $count
                        if {[dict get $result cpusetCpus] eq "" || $count<[dict get $result cpusetCpus]} {dict set result cpusetCpus $count}
                    }
                }
            }
            if {$directory eq $mount} break
            set directory [file dirname $directory]
        }
    }
}
if {!$hierarchySeen} {lappend reasons cgroup-hierarchy-unavailable}
if {!$quotaSeen || $quotaMissing} {lappend reasons quota-unavailable-or-partial}
dict set result quotaStatus [expr {[dict get $result quotaCores] ne "" ? "limited" : ($quotaSeen && !$quotaMissing ? "unlimited" : "unknown")}]
if {[llength $ceilings]} {dict set result effectiveCores [lindex [lsort -real $ceilings] 0]}
dict set result constraintsComplete [expr {$reasons eq ""}]
dict set result incompleteReasons [lsort -unique $reasons]
dict set result throttling $groups
dict set result throttlingAvailable [expr {[dict size $groups]>0}]
return $result
XQL Not present:
Generic, PostgreSQL, Oracle
[ hide source ] | [ make this the default ]
Show another procedure: