in metric/system/cgroup/cgv2/io.go [110:170]
func parseStatLine(line string, resolveDevIDs bool) ([]string, IOStat, bool, error) {
devIds := []string{}
stats := IOStat{}
foundMetrics := false
// cautiously iterate over a line to find the components
// under certain conditions, the stat.io will combine different loopback devices onto a single line,
// 7:7 7:6 7:5 7:4 rbytes=556032 wbytes=0 rios=78 wios=0 dbytes=0 dios=0
// we can also get lines without metrics, like
// 7:7 7:6 7:5 7:4
for _, component := range strings.Split(line, " ") {
if strings.Contains(component, ":") {
var major, minor uint64
_, err := fmt.Sscanf(component, "%d:%d", &major, &minor)
if err != nil {
return nil, IOStat{}, false, fmt.Errorf("could not read device ID: %s: %w", component, err)
}
var found bool
var devName string
// try to find the device name associated with the major/minor pair
// This isn't guaranteed to work, for a number of reasons, so we'll need to fall back
if resolveDevIDs {
found, devName, _ = fetchDeviceName(major, minor)
}
if found {
devIds = append(devIds, devName)
} else {
devIds = append(devIds, component)
}
} else if strings.Contains(component, "=") {
foundMetrics = true
counterSplit := strings.Split(component, "=")
if len(counterSplit) < 2 {
continue
}
name := counterSplit[0]
counter, err := strconv.ParseUint(counterSplit[1], 10, 64)
if err != nil {
return nil, IOStat{}, false, fmt.Errorf("error parsing counter '%s' in stat: %w", counterSplit[1], err)
}
switch name {
case "rbytes":
stats.Read.Bytes = counter
case "wbytes":
stats.Write.Bytes = counter
case "rios":
stats.Read.IOs = counter
case "wios":
stats.Write.IOs = counter
case "dbytes":
stats.Discarded.Bytes = counter
case "dios":
stats.Discarded.IOs = counter
}
}
}
return devIds, stats, foundMetrics, nil
}