Skip to content
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions process_metrics_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6457
var pageSizeBytes = uint64(os.Getpagesize())

var cgroupCpuStatPath = ""

func init() {
cgroupV2Path := getCgroupV2Path()
if cgroupV2Path != "" {
cgroupCpuStatPath = cgroupV2Path + "/cpu.stat"
return
}

cgroupV1CpuControllerPath := getCgroupV1CpuControllerPath()
if cgroupV1CpuControllerPath == "" {
return
}
cgroupCpuStatPath = cgroupV1CpuControllerPath + "/cpu.stat"
}

// See http://man7.org/linux/man-pages/man5/proc.5.html
type procStat struct {
State byte
Expand Down Expand Up @@ -96,6 +112,7 @@
writeProcessMemMetrics(w)
writeIOMetrics(w)
writePSIMetrics(w)
writeProcessCpuThrottleMetrics(w)
}

var procSelfIOErrLogged uint32
Expand Down Expand Up @@ -403,6 +420,62 @@
return some, full, nil
}

type cpuThrottleMetrics struct {
nrPeriods uint64
nrThrottled uint64
throttledTime uint64 // us
}

func getCgroupCpuStats() (*cpuThrottleMetrics, error) {
if cgroupCpuControllerPath == "" {

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / Build

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-386

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-s390x

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-amd64

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-arm64

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-arm

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-ppc64le

undefined: cgroupCpuControllerPath

Check failure on line 430 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / test

undefined: cgroupCpuControllerPath
return nil, nil
}
data, err := ioutil.ReadFile(cgroupCpuControllerPath)

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / Build

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-386

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-s390x

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-amd64

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-arm64

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-arm

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / linux-ppc64le

undefined: cgroupCpuControllerPath

Check failure on line 433 in process_metrics_linux.go

View workflow job for this annotation

GitHub Actions / test

undefined: cgroupCpuControllerPath
if err != nil {
return nil, err
}
var ctms cpuThrottleMetrics
lines := strings.Split(string(data), "\n")
for _, s := range lines {
field := strings.Fields(s)
if len(field) != 2 {
continue
}
fieldKey := field[0]
fieldValue := field[1]
value, err := strconv.ParseUint(fieldValue, 10, 64)
if err != nil {
return nil, fmt.Errorf("cannot parse number from %q: %w", s, err)
}
// For cgroup v1, refer to ttps://docs.kernel.org/scheduler/sched-bwc.html#statistics
// For cgroup v2, refer to https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#cpu-interface-files
switch fieldKey {
case "nr_periods":
ctms.nrPeriods = value
case "nr_throttled":
ctms.nrThrottled = value
case "throttled_usec":
// In cgroup v2, the field is in microseconds.
ctms.throttledTime = value / 1000
case "throttled_time":
// In cgroup v1, the field is in nanoseconds.
ctms.throttledTime = value / 1000 / 1000
}
}
return &ctms, nil
}

func writeProcessCpuThrottleMetrics(w io.Writer) {
ctms, err := getCgroupCpuStats()
if err != nil {
log.Printf("ERROR: metrics: cannot determine cpu.stat: %s", err)
return
}
WriteGaugeUint64(w, "process_cgroup_cpu_periods_total", ctms.nrPeriods)
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
WriteGaugeUint64(w, "process_cgroup_cpu_throttled_periods_total", ctms.nrThrottled)
WriteGaugeUint64(w, "process_cgroup_cpu_throttled_seconds_total", ctms.throttledTime)
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: process_cgroup_cpu_throttled_seconds_total reports non-second units (micro/milliseconds). Convert throttled_time to seconds (or store microseconds consistently and divide by 1e6 when writing) to avoid unit mismatch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At process_metrics_linux.go, line 476:

<comment>`process_cgroup_cpu_throttled_seconds_total` reports non-second units (micro/milliseconds). Convert throttled_time to seconds (or store microseconds consistently and divide by 1e6 when writing) to avoid unit mismatch.</comment>

<file context>
@@ -403,6 +420,62 @@ func readPSITotals(cgroupPath, statsName string) (uint64, uint64, error) {
+	}
+	WriteGaugeUint64(w, "process_cgroup_cpu_periods_total", ctms.nrPeriods)
+	WriteGaugeUint64(w, "process_cgroup_cpu_throttled_periods_total", ctms.nrThrottled)
+	WriteGaugeUint64(w, "process_cgroup_cpu_throttled_seconds_total", ctms.throttledTime)
+}
+
</file context>
Fix with Cubic

}

func getCgroupV2Path() string {
data, err := ioutil.ReadFile("/proc/self/cgroup")
if err != nil {
Expand All @@ -417,3 +490,27 @@
// Drop trailing slash if it exsits. This prevents from '//' in the constructed paths by the caller.
return strings.TrimSuffix(path, "/")
}

func getCgroupV1CpuControllerPath() string {
data, err := os.ReadFile("/proc/self/cgroup")
if err != nil {
return ""
}
lines := strings.Split(string(data), "\n")
// each line will be in the format of "hierarchy-ID:controller-list:cgroup-path"
// e.g. 5:cpuacct,cpu,cpuset:/daemons
// https://man7.org/linux/man-pages/man7/cgroups.7.html
for _, line := range lines {
parts := strings.SplitN(line, ":", 3)
if len(parts) != 3 {
continue
}
controllers := strings.Split(parts[1], ",")
for _, ctrl := range controllers {
if strings.TrimSpace(ctrl) == "cpu" {
return "/sys/fs/cgroup/cpu" + parts[2]
}
}
}
return ""
}
Loading