From acff09d1301d4438dcb7ee6c6ebd301c8d78d4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A2ni=20Marques?= Date: Fri, 10 Jul 2026 11:11:06 +0200 Subject: [PATCH] =?UTF-8?q?stats:=20mem=20snapshot=20w/o=20pipe=20(subshel?= =?UTF-8?q?l=20lost=20counters=20=E2=86=92=200=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/taler-shared/mem-snapshot.sh | 133 ++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 5 deletions(-) diff --git a/scripts/taler-shared/mem-snapshot.sh b/scripts/taler-shared/mem-snapshot.sh index e1a4b91..b011202 100644 --- a/scripts/taler-shared/mem-snapshot.sh +++ b/scripts/taler-shared/mem-snapshot.sh @@ -1,7 +1,130 @@ #!/bin/bash -# WRONG pipeline → 0 B -mem_snapshot_json() { - local s=0 - ls -d /proc/[0-9]* 2>/dev/null | while read d; do s=$((s+1)); done - MEM_JSON="{\"n\":$s}" +# Memory snapshot for landing-stats (source after json_str is defined). +# Sets MEM_JSON (fields to embed inside performance.memory). +# IMPORTANT: no pipelines around the /proc loop (bash subshell loses counters). + +mem_fmt_bytes() { + awk -v b="${1:-0}" 'BEGIN{ + b = b + 0 + if (b < 1024) printf "%d B", b + else if (b < 1048576) printf "%.1f KiB", b / 1024 + else if (b < 1073741824) printf "%.1f MiB", b / 1048576 + else printf "%.2f GiB", b / 1073741824 + }' +} + +mem_snapshot_json() { + local rss_kb rss_b pid comm cmd short + local sum_b=0 cgroup_b="" limit_b="" + local postgres_b=0 taler_b=0 nginx_b=0 java_b=0 redis_b=0 other_b=0 + local n_pg=0 n_taler=0 n_nginx=0 n_java=0 n_redis=0 n_other=0 + local allf topf top_json top_n=0 lim_json cg_json hum cjs sjs hjs + + if [ -r /sys/fs/cgroup/memory.current ]; then + cgroup_b=$(tr -d ' \n' /dev/null || true) + if [ -r /sys/fs/cgroup/memory.max ]; then + limit_b=$(tr -d ' \n' /dev/null || true) + [ "$limit_b" = "max" ] && limit_b="" + fi + elif [ -r /sys/fs/cgroup/memory/memory.usage_in_bytes ]; then + cgroup_b=$(tr -d ' \n' /dev/null || true) + fi + + allf=$(mktemp) + topf=$(mktemp) + + for st in /proc/[0-9]*/status; do + [ -f "$st" ] || continue + pid=${st#/proc/} + pid=${pid%/status} + rss_kb=$(awk '/^VmRSS:/{print $2; exit}' "$st" 2>/dev/null || true) + [[ "$rss_kb" =~ ^[0-9]+$ ]] || continue + rss_b=$((rss_kb * 1024)) + sum_b=$((sum_b + rss_b)) + comm=$(awk '/^Name:/{print $2; exit}' "$st" 2>/dev/null || echo "?") + cmd="" + if [ -r "/proc/$pid/cmdline" ]; then + cmd=$(tr '\0' ' ' <"/proc/$pid/cmdline" 2>/dev/null | sed 's/[[:space:]]*$//') + fi + [ -n "$cmd" ] || cmd=$comm + short=$(printf '%s' "$cmd" | cut -c1-100) + + case "$comm $cmd" in + *postgres*|*postmaster*) + postgres_b=$((postgres_b + rss_b)); n_pg=$((n_pg + 1)) ;; + *taler-exchange*|*taler-merchant*|*taler-auditor*|*taler-helper*|*taler_*) + taler_b=$((taler_b + rss_b)); n_taler=$((n_taler + 1)) ;; + *nginx*) + nginx_b=$((nginx_b + rss_b)); n_nginx=$((n_nginx + 1)) ;; + *java*|*libeufin*|*MainKt*) + java_b=$((java_b + rss_b)); n_java=$((n_java + 1)) ;; + *redis-server*|*redis*) + redis_b=$((redis_b + rss_b)); n_redis=$((n_redis + 1)) ;; + *) + other_b=$((other_b + rss_b)); n_other=$((n_other + 1)) ;; + esac + printf '%s\t%s\t%s\n' "$rss_b" "$comm" "$short" >>"$allf" + done + + sort -t$'\t' -nr -k1,1 "$allf" 2>/dev/null | head -10 >"$topf" + rm -f "$allf" + + local total_b=$sum_b + if [[ "$cgroup_b" =~ ^[0-9]+$ ]] && [ "$cgroup_b" -gt 0 ]; then + total_b=$cgroup_b + fi + + top_json="[" + top_n=0 + while IFS=$'\t' read -r rss_b comm short; do + [ -z "${rss_b:-}" ] && continue + [ "$top_n" -gt 0 ] && top_json="${top_json}," + top_n=$((top_n + 1)) + hum=$(mem_fmt_bytes "$rss_b") + cjs=$(json_str "$comm") + sjs=$(json_str "$short") + hjs=$(json_str "$hum") + top_json="${top_json} + {\"rss_bytes\": ${rss_b}, \"rss_human\": ${hjs}, \"comm\": ${cjs}, \"cmd\": ${sjs}}" + done <"$topf" + top_json="${top_json} + ]" + rm -f "$topf" + + lim_json="null" + if [[ "$limit_b" =~ ^[0-9]+$ ]] && [ "$limit_b" -gt 0 ]; then + lim_json=$limit_b + fi + cg_json="null" + if [[ "$cgroup_b" =~ ^[0-9]+$ ]]; then + cg_json=$cgroup_b + fi + + MEM_JSON=" + \"container_rss_bytes\": ${total_b}, + \"container_rss_human\": $(json_str "$(mem_fmt_bytes "$total_b")"), + \"proc_sum_rss_bytes\": ${sum_b}, + \"proc_sum_rss_human\": $(json_str "$(mem_fmt_bytes "$sum_b")"), + \"cgroup_bytes\": ${cg_json}, + \"cgroup_limit_bytes\": ${lim_json}, + \"postgres_rss_bytes\": ${postgres_b}, + \"postgres_rss_human\": $(json_str "$(mem_fmt_bytes "$postgres_b")"), + \"postgres_n\": ${n_pg}, + \"taler_rss_bytes\": ${taler_b}, + \"taler_rss_human\": $(json_str "$(mem_fmt_bytes "$taler_b")"), + \"taler_n\": ${n_taler}, + \"nginx_rss_bytes\": ${nginx_b}, + \"nginx_rss_human\": $(json_str "$(mem_fmt_bytes "$nginx_b")"), + \"nginx_n\": ${n_nginx}, + \"java_rss_bytes\": ${java_b}, + \"java_rss_human\": $(json_str "$(mem_fmt_bytes "$java_b")"), + \"java_n\": ${n_java}, + \"redis_rss_bytes\": ${redis_b}, + \"redis_rss_human\": $(json_str "$(mem_fmt_bytes "$redis_b")"), + \"redis_n\": ${n_redis}, + \"other_rss_bytes\": ${other_b}, + \"other_rss_human\": $(json_str "$(mem_fmt_bytes "$other_b")"), + \"other_n\": ${n_other}, + \"top\": ${top_json} +" }