handler.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. // Copyright 2018 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package libcontainer
  15. import (
  16. "bufio"
  17. "fmt"
  18. "io"
  19. "io/ioutil"
  20. "os"
  21. "path"
  22. "strconv"
  23. "strings"
  24. "time"
  25. "github.com/google/cadvisor/container"
  26. info "github.com/google/cadvisor/info/v1"
  27. "golang.org/x/sys/unix"
  28. "bytes"
  29. "github.com/opencontainers/runc/libcontainer"
  30. "github.com/opencontainers/runc/libcontainer/cgroups"
  31. "k8s.io/klog"
  32. )
  33. type Handler struct {
  34. cgroupManager cgroups.Manager
  35. rootFs string
  36. pid int
  37. includedMetrics container.MetricSet
  38. pidMetricsCache map[int]*info.CpuSchedstat
  39. }
  40. func NewHandler(cgroupManager cgroups.Manager, rootFs string, pid int, includedMetrics container.MetricSet) *Handler {
  41. return &Handler{
  42. cgroupManager: cgroupManager,
  43. rootFs: rootFs,
  44. pid: pid,
  45. includedMetrics: includedMetrics,
  46. pidMetricsCache: make(map[int]*info.CpuSchedstat),
  47. }
  48. }
  49. // Get cgroup and networking stats of the specified container
  50. func (h *Handler) GetStats() (*info.ContainerStats, error) {
  51. cgroupStats, err := h.cgroupManager.GetStats()
  52. if err != nil {
  53. return nil, err
  54. }
  55. libcontainerStats := &libcontainer.Stats{
  56. CgroupStats: cgroupStats,
  57. }
  58. stats := newContainerStats(libcontainerStats, h.includedMetrics)
  59. if h.includedMetrics.Has(container.ProcessSchedulerMetrics) {
  60. pids, err := h.cgroupManager.GetAllPids()
  61. if err != nil {
  62. klog.V(4).Infof("Could not get PIDs for container %d: %v", h.pid, err)
  63. } else {
  64. stats.Cpu.Schedstat, err = schedulerStatsFromProcs(h.rootFs, pids, h.pidMetricsCache)
  65. if err != nil {
  66. klog.V(4).Infof("Unable to get Process Scheduler Stats: %v", err)
  67. }
  68. }
  69. }
  70. // If we know the pid then get network stats from /proc/<pid>/net/dev
  71. if h.pid == 0 {
  72. return stats, nil
  73. }
  74. if h.includedMetrics.Has(container.NetworkUsageMetrics) {
  75. netStats, err := networkStatsFromProc(h.rootFs, h.pid)
  76. if err != nil {
  77. klog.V(4).Infof("Unable to get network stats from pid %d: %v", h.pid, err)
  78. } else {
  79. stats.Network.Interfaces = append(stats.Network.Interfaces, netStats...)
  80. }
  81. }
  82. if h.includedMetrics.Has(container.NetworkTcpUsageMetrics) {
  83. t, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp")
  84. if err != nil {
  85. klog.V(4).Infof("Unable to get tcp stats from pid %d: %v", h.pid, err)
  86. } else {
  87. stats.Network.Tcp = t
  88. }
  89. t6, err := tcpStatsFromProc(h.rootFs, h.pid, "net/tcp6")
  90. if err != nil {
  91. klog.V(4).Infof("Unable to get tcp6 stats from pid %d: %v", h.pid, err)
  92. } else {
  93. stats.Network.Tcp6 = t6
  94. }
  95. }
  96. if h.includedMetrics.Has(container.NetworkUdpUsageMetrics) {
  97. u, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp")
  98. if err != nil {
  99. klog.V(4).Infof("Unable to get udp stats from pid %d: %v", h.pid, err)
  100. } else {
  101. stats.Network.Udp = u
  102. }
  103. u6, err := udpStatsFromProc(h.rootFs, h.pid, "net/udp6")
  104. if err != nil {
  105. klog.V(4).Infof("Unable to get udp6 stats from pid %d: %v", h.pid, err)
  106. } else {
  107. stats.Network.Udp6 = u6
  108. }
  109. }
  110. if h.includedMetrics.Has(container.ProcessMetrics) {
  111. paths := h.cgroupManager.GetPaths()
  112. path, ok := paths["cpu"]
  113. if !ok {
  114. klog.V(4).Infof("Could not find cgroups CPU for container %d", h.pid)
  115. } else {
  116. stats.Processes, err = processStatsFromProcs(h.rootFs, path)
  117. if err != nil {
  118. klog.V(4).Infof("Unable to get Process Stats: %v", err)
  119. }
  120. }
  121. // if include processes metrics, just set threads metrics if exist, and has no relationship with cpu path
  122. setThreadsStats(cgroupStats, stats)
  123. }
  124. // For backwards compatibility.
  125. if len(stats.Network.Interfaces) > 0 {
  126. stats.Network.InterfaceStats = stats.Network.Interfaces[0]
  127. }
  128. return stats, nil
  129. }
  130. func processStatsFromProcs(rootFs string, cgroupPath string) (info.ProcessStats, error) {
  131. var fdCount, socketCount uint64
  132. filePath := path.Join(cgroupPath, "cgroup.procs")
  133. out, err := ioutil.ReadFile(filePath)
  134. if err != nil {
  135. return info.ProcessStats{}, fmt.Errorf("couldn't open cpu cgroup procs file %v : %v", filePath, err)
  136. }
  137. pids := strings.Split(string(out), "\n")
  138. // EOL is also treated as a new line while reading "cgroup.procs" file with ioutil.ReadFile.
  139. // The last value is an empty string "". Ex: pids = ["22", "1223", ""]
  140. // Trim the last value
  141. if len(pids) != 0 && pids[len(pids)-1] == "" {
  142. pids = pids[:len(pids)-1]
  143. }
  144. for _, pid := range pids {
  145. dirPath := path.Join(rootFs, "/proc", pid, "fd")
  146. fds, err := ioutil.ReadDir(dirPath)
  147. if err != nil {
  148. klog.V(4).Infof("error while listing directory %q to measure fd count: %v", dirPath, err)
  149. continue
  150. }
  151. fdCount += uint64(len(fds))
  152. for _, fd := range fds {
  153. fdPath := path.Join(dirPath, fd.Name())
  154. linkName, err := os.Readlink(fdPath)
  155. if err != nil {
  156. klog.V(4).Infof("error while reading %q link: %v", fdPath, err)
  157. continue
  158. }
  159. if strings.HasPrefix(linkName, "socket") {
  160. socketCount++
  161. }
  162. }
  163. }
  164. processStats := info.ProcessStats{
  165. ProcessCount: uint64(len(pids)),
  166. FdCount: fdCount,
  167. SocketCount: socketCount,
  168. }
  169. return processStats, nil
  170. }
  171. func schedulerStatsFromProcs(rootFs string, pids []int, pidMetricsCache map[int]*info.CpuSchedstat) (info.CpuSchedstat, error) {
  172. for _, pid := range pids {
  173. f, err := os.Open(path.Join(rootFs, "proc", strconv.Itoa(pid), "schedstat"))
  174. if err != nil {
  175. return info.CpuSchedstat{}, fmt.Errorf("couldn't open scheduler statistics for process %d: %v", pid, err)
  176. }
  177. defer f.Close()
  178. contents, err := ioutil.ReadAll(f)
  179. if err != nil {
  180. return info.CpuSchedstat{}, fmt.Errorf("couldn't read scheduler statistics for process %d: %v", pid, err)
  181. }
  182. rawMetrics := bytes.Split(bytes.TrimRight(contents, "\n"), []byte(" "))
  183. if len(rawMetrics) != 3 {
  184. return info.CpuSchedstat{}, fmt.Errorf("unexpected number of metrics in schedstat file for process %d", pid)
  185. }
  186. cacheEntry, ok := pidMetricsCache[pid]
  187. if !ok {
  188. cacheEntry = &info.CpuSchedstat{}
  189. pidMetricsCache[pid] = cacheEntry
  190. }
  191. for i, rawMetric := range rawMetrics {
  192. metric, err := strconv.ParseUint(string(rawMetric), 10, 64)
  193. if err != nil {
  194. return info.CpuSchedstat{}, fmt.Errorf("parsing error while reading scheduler statistics for process: %d: %v", pid, err)
  195. }
  196. switch i {
  197. case 0:
  198. cacheEntry.RunTime = metric
  199. case 1:
  200. cacheEntry.RunqueueTime = metric
  201. case 2:
  202. cacheEntry.RunPeriods = metric
  203. }
  204. }
  205. }
  206. schedstats := info.CpuSchedstat{}
  207. for _, v := range pidMetricsCache {
  208. schedstats.RunPeriods += v.RunPeriods
  209. schedstats.RunqueueTime += v.RunqueueTime
  210. schedstats.RunTime += v.RunTime
  211. }
  212. return schedstats, nil
  213. }
  214. func networkStatsFromProc(rootFs string, pid int) ([]info.InterfaceStats, error) {
  215. netStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), "/net/dev")
  216. ifaceStats, err := scanInterfaceStats(netStatsFile)
  217. if err != nil {
  218. return []info.InterfaceStats{}, fmt.Errorf("couldn't read network stats: %v", err)
  219. }
  220. return ifaceStats, nil
  221. }
  222. var (
  223. ignoredDevicePrefixes = []string{"lo", "veth", "docker"}
  224. )
  225. func isIgnoredDevice(ifName string) bool {
  226. for _, prefix := range ignoredDevicePrefixes {
  227. if strings.HasPrefix(strings.ToLower(ifName), prefix) {
  228. return true
  229. }
  230. }
  231. return false
  232. }
  233. func scanInterfaceStats(netStatsFile string) ([]info.InterfaceStats, error) {
  234. file, err := os.Open(netStatsFile)
  235. if err != nil {
  236. return nil, fmt.Errorf("failure opening %s: %v", netStatsFile, err)
  237. }
  238. defer file.Close()
  239. scanner := bufio.NewScanner(file)
  240. // Discard header lines
  241. for i := 0; i < 2; i++ {
  242. if b := scanner.Scan(); !b {
  243. return nil, scanner.Err()
  244. }
  245. }
  246. stats := []info.InterfaceStats{}
  247. for scanner.Scan() {
  248. line := scanner.Text()
  249. line = strings.Replace(line, ":", "", -1)
  250. fields := strings.Fields(line)
  251. // If the format of the line is invalid then don't trust any of the stats
  252. // in this file.
  253. if len(fields) != 17 {
  254. return nil, fmt.Errorf("invalid interface stats line: %v", line)
  255. }
  256. devName := fields[0]
  257. if isIgnoredDevice(devName) {
  258. continue
  259. }
  260. i := info.InterfaceStats{
  261. Name: devName,
  262. }
  263. statFields := append(fields[1:5], fields[9:13]...)
  264. statPointers := []*uint64{
  265. &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped,
  266. &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped,
  267. }
  268. err := setInterfaceStatValues(statFields, statPointers)
  269. if err != nil {
  270. return nil, fmt.Errorf("cannot parse interface stats (%v): %v", err, line)
  271. }
  272. stats = append(stats, i)
  273. }
  274. return stats, nil
  275. }
  276. func setInterfaceStatValues(fields []string, pointers []*uint64) error {
  277. for i, v := range fields {
  278. val, err := strconv.ParseUint(v, 10, 64)
  279. if err != nil {
  280. return err
  281. }
  282. *pointers[i] = val
  283. }
  284. return nil
  285. }
  286. func tcpStatsFromProc(rootFs string, pid int, file string) (info.TcpStat, error) {
  287. tcpStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file)
  288. tcpStats, err := scanTcpStats(tcpStatsFile)
  289. if err != nil {
  290. return tcpStats, fmt.Errorf("couldn't read tcp stats: %v", err)
  291. }
  292. return tcpStats, nil
  293. }
  294. func scanTcpStats(tcpStatsFile string) (info.TcpStat, error) {
  295. var stats info.TcpStat
  296. data, err := ioutil.ReadFile(tcpStatsFile)
  297. if err != nil {
  298. return stats, fmt.Errorf("failure opening %s: %v", tcpStatsFile, err)
  299. }
  300. tcpStateMap := map[string]uint64{
  301. "01": 0, //ESTABLISHED
  302. "02": 0, //SYN_SENT
  303. "03": 0, //SYN_RECV
  304. "04": 0, //FIN_WAIT1
  305. "05": 0, //FIN_WAIT2
  306. "06": 0, //TIME_WAIT
  307. "07": 0, //CLOSE
  308. "08": 0, //CLOSE_WAIT
  309. "09": 0, //LAST_ACK
  310. "0A": 0, //LISTEN
  311. "0B": 0, //CLOSING
  312. }
  313. reader := strings.NewReader(string(data))
  314. scanner := bufio.NewScanner(reader)
  315. scanner.Split(bufio.ScanLines)
  316. // Discard header line
  317. if b := scanner.Scan(); !b {
  318. return stats, scanner.Err()
  319. }
  320. for scanner.Scan() {
  321. line := scanner.Text()
  322. state := strings.Fields(line)
  323. // TCP state is the 4th field.
  324. // Format: sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode
  325. tcpState := state[3]
  326. _, ok := tcpStateMap[tcpState]
  327. if !ok {
  328. return stats, fmt.Errorf("invalid TCP stats line: %v", line)
  329. }
  330. tcpStateMap[tcpState]++
  331. }
  332. stats = info.TcpStat{
  333. Established: tcpStateMap["01"],
  334. SynSent: tcpStateMap["02"],
  335. SynRecv: tcpStateMap["03"],
  336. FinWait1: tcpStateMap["04"],
  337. FinWait2: tcpStateMap["05"],
  338. TimeWait: tcpStateMap["06"],
  339. Close: tcpStateMap["07"],
  340. CloseWait: tcpStateMap["08"],
  341. LastAck: tcpStateMap["09"],
  342. Listen: tcpStateMap["0A"],
  343. Closing: tcpStateMap["0B"],
  344. }
  345. return stats, nil
  346. }
  347. func udpStatsFromProc(rootFs string, pid int, file string) (info.UdpStat, error) {
  348. var err error
  349. var udpStats info.UdpStat
  350. udpStatsFile := path.Join(rootFs, "proc", strconv.Itoa(pid), file)
  351. r, err := os.Open(udpStatsFile)
  352. if err != nil {
  353. return udpStats, fmt.Errorf("failure opening %s: %v", udpStatsFile, err)
  354. }
  355. udpStats, err = scanUdpStats(r)
  356. if err != nil {
  357. return udpStats, fmt.Errorf("couldn't read udp stats: %v", err)
  358. }
  359. return udpStats, nil
  360. }
  361. func scanUdpStats(r io.Reader) (info.UdpStat, error) {
  362. var stats info.UdpStat
  363. scanner := bufio.NewScanner(r)
  364. scanner.Split(bufio.ScanLines)
  365. // Discard header line
  366. if b := scanner.Scan(); !b {
  367. return stats, scanner.Err()
  368. }
  369. listening := uint64(0)
  370. dropped := uint64(0)
  371. rxQueued := uint64(0)
  372. txQueued := uint64(0)
  373. for scanner.Scan() {
  374. line := scanner.Text()
  375. // Format: sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops
  376. listening++
  377. fs := strings.Fields(line)
  378. if len(fs) != 13 {
  379. continue
  380. }
  381. rx, tx := uint64(0), uint64(0)
  382. fmt.Sscanf(fs[4], "%X:%X", &rx, &tx)
  383. rxQueued += rx
  384. txQueued += tx
  385. d, err := strconv.Atoi(string(fs[12]))
  386. if err != nil {
  387. continue
  388. }
  389. dropped += uint64(d)
  390. }
  391. stats = info.UdpStat{
  392. Listen: listening,
  393. Dropped: dropped,
  394. RxQueued: rxQueued,
  395. TxQueued: txQueued,
  396. }
  397. return stats, nil
  398. }
  399. func (h *Handler) GetProcesses() ([]int, error) {
  400. pids, err := h.cgroupManager.GetPids()
  401. if err != nil {
  402. return nil, err
  403. }
  404. return pids, nil
  405. }
  406. func minUint32(x, y uint32) uint32 {
  407. if x < y {
  408. return x
  409. }
  410. return y
  411. }
  412. // var to allow unit tests to stub it out
  413. var numCpusFunc = getNumberOnlineCPUs
  414. // Convert libcontainer stats to info.ContainerStats.
  415. func setCpuStats(s *cgroups.Stats, ret *info.ContainerStats, withPerCPU bool) {
  416. ret.Cpu.Usage.User = s.CpuStats.CpuUsage.UsageInUsermode
  417. ret.Cpu.Usage.System = s.CpuStats.CpuUsage.UsageInKernelmode
  418. ret.Cpu.Usage.Total = s.CpuStats.CpuUsage.TotalUsage
  419. ret.Cpu.CFS.Periods = s.CpuStats.ThrottlingData.Periods
  420. ret.Cpu.CFS.ThrottledPeriods = s.CpuStats.ThrottlingData.ThrottledPeriods
  421. ret.Cpu.CFS.ThrottledTime = s.CpuStats.ThrottlingData.ThrottledTime
  422. if !withPerCPU {
  423. return
  424. }
  425. if len(s.CpuStats.CpuUsage.PercpuUsage) == 0 {
  426. // libcontainer's 'GetStats' can leave 'PercpuUsage' nil if it skipped the
  427. // cpuacct subsystem.
  428. return
  429. }
  430. numPossible := uint32(len(s.CpuStats.CpuUsage.PercpuUsage))
  431. // Note that as of https://patchwork.kernel.org/patch/8607101/ (kernel v4.7),
  432. // the percpu usage information includes extra zero values for all additional
  433. // possible CPUs. This is to allow statistic collection after CPU-hotplug.
  434. // We intentionally ignore these extra zeroes.
  435. numActual, err := numCpusFunc()
  436. if err != nil {
  437. klog.Errorf("unable to determine number of actual cpus; defaulting to maximum possible number: errno %v", err)
  438. numActual = numPossible
  439. }
  440. if numActual > numPossible {
  441. // The real number of cores should never be greater than the number of
  442. // datapoints reported in cpu usage.
  443. klog.Errorf("PercpuUsage had %v cpus, but the actual number is %v; ignoring extra CPUs", numPossible, numActual)
  444. }
  445. numActual = minUint32(numPossible, numActual)
  446. ret.Cpu.Usage.PerCpu = make([]uint64, numActual)
  447. for i := uint32(0); i < numActual; i++ {
  448. ret.Cpu.Usage.PerCpu[i] = s.CpuStats.CpuUsage.PercpuUsage[i]
  449. }
  450. }
  451. func getNumberOnlineCPUs() (uint32, error) {
  452. var availableCPUs unix.CPUSet
  453. if err := unix.SchedGetaffinity(0, &availableCPUs); err != nil {
  454. return 0, err
  455. }
  456. return uint32(availableCPUs.Count()), nil
  457. }
  458. func setDiskIoStats(s *cgroups.Stats, ret *info.ContainerStats) {
  459. ret.DiskIo.IoServiceBytes = DiskStatsCopy(s.BlkioStats.IoServiceBytesRecursive)
  460. ret.DiskIo.IoServiced = DiskStatsCopy(s.BlkioStats.IoServicedRecursive)
  461. ret.DiskIo.IoQueued = DiskStatsCopy(s.BlkioStats.IoQueuedRecursive)
  462. ret.DiskIo.Sectors = DiskStatsCopy(s.BlkioStats.SectorsRecursive)
  463. ret.DiskIo.IoServiceTime = DiskStatsCopy(s.BlkioStats.IoServiceTimeRecursive)
  464. ret.DiskIo.IoWaitTime = DiskStatsCopy(s.BlkioStats.IoWaitTimeRecursive)
  465. ret.DiskIo.IoMerged = DiskStatsCopy(s.BlkioStats.IoMergedRecursive)
  466. ret.DiskIo.IoTime = DiskStatsCopy(s.BlkioStats.IoTimeRecursive)
  467. }
  468. func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) {
  469. ret.Memory.Usage = s.MemoryStats.Usage.Usage
  470. ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage
  471. ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt
  472. if s.MemoryStats.UseHierarchy {
  473. ret.Memory.Cache = s.MemoryStats.Stats["total_cache"]
  474. ret.Memory.RSS = s.MemoryStats.Stats["total_rss"]
  475. ret.Memory.Swap = s.MemoryStats.Stats["total_swap"]
  476. ret.Memory.MappedFile = s.MemoryStats.Stats["total_mapped_file"]
  477. } else {
  478. ret.Memory.Cache = s.MemoryStats.Stats["cache"]
  479. ret.Memory.RSS = s.MemoryStats.Stats["rss"]
  480. ret.Memory.Swap = s.MemoryStats.Stats["swap"]
  481. ret.Memory.MappedFile = s.MemoryStats.Stats["mapped_file"]
  482. }
  483. if v, ok := s.MemoryStats.Stats["pgfault"]; ok {
  484. ret.Memory.ContainerData.Pgfault = v
  485. ret.Memory.HierarchicalData.Pgfault = v
  486. }
  487. if v, ok := s.MemoryStats.Stats["pgmajfault"]; ok {
  488. ret.Memory.ContainerData.Pgmajfault = v
  489. ret.Memory.HierarchicalData.Pgmajfault = v
  490. }
  491. workingSet := ret.Memory.Usage
  492. if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok {
  493. if workingSet < v {
  494. workingSet = 0
  495. } else {
  496. workingSet -= v
  497. }
  498. }
  499. ret.Memory.WorkingSet = workingSet
  500. }
  501. func setNetworkStats(libcontainerStats *libcontainer.Stats, ret *info.ContainerStats) {
  502. ret.Network.Interfaces = make([]info.InterfaceStats, len(libcontainerStats.Interfaces))
  503. for i := range libcontainerStats.Interfaces {
  504. ret.Network.Interfaces[i] = info.InterfaceStats{
  505. Name: libcontainerStats.Interfaces[i].Name,
  506. RxBytes: libcontainerStats.Interfaces[i].RxBytes,
  507. RxPackets: libcontainerStats.Interfaces[i].RxPackets,
  508. RxErrors: libcontainerStats.Interfaces[i].RxErrors,
  509. RxDropped: libcontainerStats.Interfaces[i].RxDropped,
  510. TxBytes: libcontainerStats.Interfaces[i].TxBytes,
  511. TxPackets: libcontainerStats.Interfaces[i].TxPackets,
  512. TxErrors: libcontainerStats.Interfaces[i].TxErrors,
  513. TxDropped: libcontainerStats.Interfaces[i].TxDropped,
  514. }
  515. }
  516. // Add to base struct for backwards compatibility.
  517. if len(ret.Network.Interfaces) > 0 {
  518. ret.Network.InterfaceStats = ret.Network.Interfaces[0]
  519. }
  520. }
  521. // read from pids path not cpu
  522. func setThreadsStats(s *cgroups.Stats, ret *info.ContainerStats) {
  523. if s != nil {
  524. ret.Processes.ThreadsCurrent = s.PidsStats.Current
  525. ret.Processes.ThreadsMax = s.PidsStats.Limit
  526. }
  527. }
  528. func newContainerStats(libcontainerStats *libcontainer.Stats, includedMetrics container.MetricSet) *info.ContainerStats {
  529. ret := &info.ContainerStats{
  530. Timestamp: time.Now(),
  531. }
  532. if s := libcontainerStats.CgroupStats; s != nil {
  533. setCpuStats(s, ret, includedMetrics.Has(container.PerCpuUsageMetrics))
  534. if includedMetrics.Has(container.DiskIOMetrics) {
  535. setDiskIoStats(s, ret)
  536. }
  537. setMemoryStats(s, ret)
  538. }
  539. if len(libcontainerStats.Interfaces) > 0 {
  540. setNetworkStats(libcontainerStats, ret)
  541. }
  542. return ret
  543. }