in container/libcontainer/handler.go [75:192]
func (h *Handler) GetStats() (*info.ContainerStats, error) {
ignoreStatsError := false
if cgroups.IsCgroup2UnifiedMode() {
// On cgroup v2 the root cgroup stats have been introduced in recent kernel versions,
// so not all kernel versions have all the data. This means that stat fetching can fail
// due to lacking cgroup stat files, but that some data is provided.
if h.cgroupManager.Path("") == fs2.UnifiedMountpoint {
ignoreStatsError = true
}
}
cgroupStats, err := h.cgroupManager.GetStats()
if err != nil {
if !ignoreStatsError {
return nil, err
}
klog.V(4).Infof("Ignoring errors when gathering stats for root cgroup since some controllers don't have stats on the root cgroup: %v", err)
}
libcontainerStats := &libcontainer.Stats{
CgroupStats: cgroupStats,
}
stats := newContainerStats(libcontainerStats, h.includedMetrics)
if h.includedMetrics.Has(container.ProcessSchedulerMetrics) {
stats.Cpu.Schedstat, err = h.schedulerStatsFromProcs()
if err != nil {
klog.V(4).Infof("Unable to get Process Scheduler Stats: %v", err)
}
}
if h.includedMetrics.Has(container.ReferencedMemoryMetrics) {
h.cycles++
pids, err := h.cgroupManager.GetPids()
if err != nil {
klog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err)
} else {
stats.ReferencedMemory, err = referencedBytesStat(pids, h.cycles, *referencedResetInterval)
if err != nil {
klog.V(4).Infof("Unable to get referenced bytes: %v", err)
}
}
}
// If we know the pid then get network stats from /proc/<pid>/net/dev
if h.pid > 0 {
if h.includedMetrics.Has(container.NetworkUsageMetrics) {
netStats, err := networkStatsFromProc(h.rootFs, h.pid)
if err != nil {
klog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err)
} else {
stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...)
}
}
if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) {
t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp")
if err != nil {
klog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err)
} else {
stats.Network.Tcp = t
}
t6, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp6")
if err != nil {
klog.V(4).Infof("Unable to get tcp6 stats from pid %d: %v", h.pid, err)
} else {
stats.Network.Tcp6 = t6
}
}
if h.includedMetrics.Has(container.NetworkAdvancedTcpUsageMetrics) {
ta, err := advancedTCPStatsFromProc(h.rootFs, h.pid, "net/netstat", "net/snmp")
if err != nil {
klog.V(4).Infof("Unable to get advanced tcp stats from pid %d: %v", h.pid, err)
} else {
stats.Network.TcpAdvanced = ta
}
}
if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) {
u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp")
if err != nil {
klog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err)
} else {
stats.Network.Udp = u
}
u6, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp6")
if err != nil {
klog.V(4).Infof("Unable to get udp6 stats from pid %d: %v", h.pid, err)
} else {
stats.Network.Udp6 = u6
}
}
}
// some process metrics are per container ( number of processes, number of
// file descriptors etc.) and not required a proper container's
// root PID (systemd services don't have the root PID atm)
if h.includedMetrics.Has(container.ProcessMetrics) {
path, ok := common.GetControllerPath(h.cgroupManager.GetPaths(), "cpu", cgroups.IsCgroup2UnifiedMode())
if !ok {
klog.V(4).Infof("Could not find cgroups CPU for container %d", h.pid)
} else {
stats.Processes, err = processStatsFromProcs(h.rootFs, path, h.pid)
if err != nil {
klog.V(4).Infof("Unable to get Process Stats: %v", err)
}
}
// if include processes metrics, just set threads metrics if exist, and has no relationship with cpu path
setThreadsStats(cgroupStats, stats)
}
// For backwards compatibility.
if len(stats.Network.Interfaces) > 0 {
stats.Network.InterfaceStats = stats.Network.Interfaces[0]
}
return stats, nil
}