Skip to content
Merged
Changes from all commits
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
170 changes: 106 additions & 64 deletions gz-plugins/cpu_detailed.prom
Original file line number Diff line number Diff line change
Expand Up @@ -30,67 +30,109 @@ if [ -z "$cpu_stats" ]; then
exit 0
fi

# Get list of CPU IDs
cpu_ids=$(psrinfo | cut -f1)

# Parse cpu_stats once into associative arrays
declare -A user_sec
declare -A kernel_sec
declare -A idle_sec
declare -A intr_sec

while IFS=$'\t' read -r key value; do
if [[ $key =~ ^cpu:([0-9]+):sys:cpu_nsec_user$ ]]; then
value_sec=$(awk "BEGIN {printf \"%.9f\", $value / 1000000000}")
user_sec[${BASH_REMATCH[1]}]=$value_sec
elif [[ $key =~ ^cpu:([0-9]+):sys:cpu_nsec_kernel$ ]]; then
value_sec=$(awk "BEGIN {printf \"%.9f\", $value / 1000000000}")
kernel_sec[${BASH_REMATCH[1]}]=$value_sec
elif [[ $key =~ ^cpu:([0-9]+):sys:cpu_nsec_idle$ ]]; then
value_sec=$(awk "BEGIN {printf \"%.9f\", $value / 1000000000}")
idle_sec[${BASH_REMATCH[1]}]=$value_sec
elif [[ $key =~ ^cpu:([0-9]+):sys:cpu_nsec_intr$ ]]; then
value_sec=$(awk "BEGIN {printf \"%.9f\", $value / 1000000000}")
intr_sec[${BASH_REMATCH[1]}]=$value_sec
fi
done <<< "$cpu_stats"

# Set TTL to 0 seconds to ensure accurate rate calculations
printf '# OPTION ttl 0\n'

# Output metric headers
printf '# HELP cn_cpu_core_user_seconds_total CPU time spent in user mode per core\n'
printf '# TYPE cn_cpu_core_user_seconds_total counter\n'
printf '# HELP cn_cpu_core_system_seconds_total CPU time spent in system mode per core\n'
printf '# TYPE cn_cpu_core_system_seconds_total counter\n'
printf '# HELP cn_cpu_core_idle_seconds_total CPU time spent idle per core\n'
printf '# TYPE cn_cpu_core_idle_seconds_total counter\n'
printf '# HELP cn_cpu_core_interrupt_seconds_total CPU time spent servicing interrupts per core\n'
printf '# TYPE cn_cpu_core_interrupt_seconds_total counter\n'

# Loop through CPUs and output all metrics using array lookups
for cpu_id in $cpu_ids; do
if [ -n "${user_sec[$cpu_id]}" ]; then
printf 'cn_cpu_core_user_seconds_total{core="%s"}\t%s\n' "$cpu_id" "${user_sec[$cpu_id]}"
fi
if [ -n "${kernel_sec[$cpu_id]}" ]; then
printf 'cn_cpu_core_system_seconds_total{core="%s"}\t%s\n' "$cpu_id" "${kernel_sec[$cpu_id]}"
fi
if [ -n "${idle_sec[$cpu_id]}" ]; then
printf 'cn_cpu_core_idle_seconds_total{core="%s"}\t%s\n' "$cpu_id" "${idle_sec[$cpu_id]}"
fi
if [ -n "${intr_sec[$cpu_id]}" ]; then
printf 'cn_cpu_core_interrupt_seconds_total{core="%s"}\t%s\n' "$cpu_id" "${intr_sec[$cpu_id]}"
fi
done

# Output CPU info metrics (only need to output once, use cpu 0)
brand=$(echo "$cpu_stats" | grep "^cpu_info:0:" | grep ':brand' | cut -f2-)
chip_id=$(echo "$cpu_stats" | grep "^cpu_info:0:" | grep ':chip_id' | awk '{print $2}')
clock_mhz=$(echo "$cpu_stats" | grep "^cpu_info:0:" | grep ':clock_MHz' | awk '{print $2}')

if [ -n "$brand" ] && [ -n "$chip_id" ] && [ -n "$clock_mhz" ]; then
printf '# HELP cn_cpu_info CPU information\n'
printf '# TYPE cn_cpu_info gauge\n'
printf 'cn_cpu_info{brand="%s",chip_id="%s",clock_mhz="%s"}\t1\n' "$brand" "$chip_id" "$clock_mhz"
fi
# Parse and output all metrics using a single awk script
echo "$cpu_stats" | awk '
BEGIN {
FS = "\t"
cpu_order_count = 0
}

function extract_cpu_id(key, prefix) {
sub(prefix, "", key)
return key
}

function shell_escape(s) {
gsub(/\\/, "\\\\", s)
gsub(/"/, "\\\"", s)
return s
}

function record_cpu_order(cpu_id) {
# Track CPU order - only add if we havent seen this CPU before
if (!(cpu_id in cpu_seen)) {
cpu_seen[cpu_id] = 1
cpu_order[++cpu_order_count] = cpu_id
}
}

/^cpu:[0-9]+:sys:cpu_nsec_user\t/ {
cpu_id = extract_cpu_id($1, "cpu:")
sub(/:sys:cpu_nsec_user$/, "", cpu_id)
record_cpu_order(cpu_id)
user_sec[cpu_id] = sprintf("%.9f", $2 / 1000000000)
}

/^cpu:[0-9]+:sys:cpu_nsec_kernel\t/ {
cpu_id = extract_cpu_id($1, "cpu:")
sub(/:sys:cpu_nsec_kernel$/, "", cpu_id)
record_cpu_order(cpu_id)
kernel_sec[cpu_id] = sprintf("%.9f", $2 / 1000000000)
}

/^cpu:[0-9]+:sys:cpu_nsec_idle\t/ {
cpu_id = extract_cpu_id($1, "cpu:")
sub(/:sys:cpu_nsec_idle$/, "", cpu_id)
record_cpu_order(cpu_id)
idle_sec[cpu_id] = sprintf("%.9f", $2 / 1000000000)
}

/^cpu:[0-9]+:sys:cpu_nsec_intr\t/ {
cpu_id = extract_cpu_id($1, "cpu:")
sub(/:sys:cpu_nsec_intr$/, "", cpu_id)
record_cpu_order(cpu_id)
intr_sec[cpu_id] = sprintf("%.9f", $2 / 1000000000)
}

# CPU info from cpu 0
/^cpu_info:0:/ && /:brand\t/ {
brand = shell_escape($2)
}

/^cpu_info:0:/ && /:chip_id\t/ {
chip_id = $2
}

/^cpu_info:0:/ && /:clock_MHz\t/ {
clock_mhz = $2
}

END {
# Print TTL option
printf "# OPTION ttl 0\n"

# Print metric headers
printf "# HELP cn_cpu_core_user_seconds_total CPU time spent in user mode per core\n"
printf "# TYPE cn_cpu_core_user_seconds_total counter\n"
printf "# HELP cn_cpu_core_system_seconds_total CPU time spent in system mode per core\n"
printf "# TYPE cn_cpu_core_system_seconds_total counter\n"
printf "# HELP cn_cpu_core_idle_seconds_total CPU time spent idle per core\n"
printf "# TYPE cn_cpu_core_idle_seconds_total counter\n"
printf "# HELP cn_cpu_core_interrupt_seconds_total CPU time spent servicing interrupts per core\n"
printf "# TYPE cn_cpu_core_interrupt_seconds_total counter\n"

# Output per-CPU metrics in the order from kstat
for (i = 1; i <= cpu_order_count; i++) {
cpu_id = cpu_order[i]

if (cpu_id in user_sec) {
printf "cn_cpu_core_user_seconds_total{core=\"%s\"}\t%s\n", cpu_id, user_sec[cpu_id]
}
if (cpu_id in kernel_sec) {
printf "cn_cpu_core_system_seconds_total{core=\"%s\"}\t%s\n", cpu_id, kernel_sec[cpu_id]
}
if (cpu_id in idle_sec) {
printf "cn_cpu_core_idle_seconds_total{core=\"%s\"}\t%s\n", cpu_id, idle_sec[cpu_id]
}
if (cpu_id in intr_sec) {
printf "cn_cpu_core_interrupt_seconds_total{core=\"%s\"}\t%s\n", cpu_id, intr_sec[cpu_id]
}
}

# Output CPU info metric
if (brand != "" && chip_id != "" && clock_mhz != "") {
printf "# HELP cn_cpu_info CPU information\n"
printf "# TYPE cn_cpu_info gauge\n"
printf "cn_cpu_info{brand=\"%s\",chip_id=\"%s\",clock_mhz=\"%s\"}\t1\n", brand, chip_id, clock_mhz
}
}'