Project

General

Profile

appcds_mem_pid.sh

Roger Borrello, 06/17/2026 02:48 PM

Download (4.62 KB)

 
1
#!/bin/bash
2

    
3
# Snapshot memory of an ALREADY-RUNNING client (e.g. a spawned webClient) by PID,
4
# and accumulate rows across runs so you can compare configs that each require a
5
# server bounce in between.
6
#
7
# Unlike appcds_mem.sh (which launches the client itself), this samples processes
8
# you point it at. Typical webClient workflow:
9
#
10
#   1. Configure directory.xml for the FIRST config (e.g. AppCDS on), start server.
11
#   2. Open https://localhost:8443/chui ; find the spawned client PID, e.g.:
12
#         pgrep -fa com.goldencode.p2j.main.ClientDriver
13
#   3. ./appcds_mem_pid.sh --reset appcds <pid>
14
#   4. Stop server, change directory.xml for the SECOND config (e.g. --no-appcds:
15
#      drop -XX:SharedArchiveFile from clientConfig/jvmArgs), restart, relaunch.
16
#   5. ./appcds_mem_pid.sh no-appcds <newpid>
17
#
18
# Each run samples the PID(s) for a settle window (tracking peak RSS), reads the
19
# smaps_rollup breakdown, checks whether the .jsa is mapped, appends a row to a
20
# small store file, and reprints the accumulated table.
21
#
22
# Usage:
23
#   ./appcds_mem_pid.sh [--settle N] [--reset] [--store FILE] <label> <pid> [pid...]
24
#     --settle N   seconds to sample (track peak); 0 = single snapshot   (default 8)
25
#     --reset      clear the store before adding this row (start a fresh comparison)
26
#     --store F    accumulation file                       (default <scriptdir>/.appcds_mem_pid.tsv)
27
#   Pass multiple PIDs to aggregate (e.g. several concurrent webClients).
28

    
29
srcpath="$(cd "$(dirname "$0")"; pwd)"
30
settle=8
31
reset=
32
store="$srcpath/.appcds_mem_pid.tsv"
33
args=()
34
while [[ $# -gt 0 ]]; do
35
   case "$1" in
36
      --settle) settle="$2"; shift 2 ;;
37
      --reset)  reset=1; shift ;;
38
      --store)  store="$2"; shift 2 ;;
39
      *)        args+=("$1"); shift ;;
40
   esac
41
done
42

    
43
label="${args[0]}"
44
pids=("${args[@]:1}")
45
if [ -z "$label" ] || [ "${#pids[@]}" -eq 0 ]; then
46
   echo "Usage: $0 [--settle N] [--reset] [--store FILE] <label> <pid> [pid...]" >&2
47
   exit 1
48
fi
49

    
50
mb()       { awk -v k="${1:-0}" 'BEGIN{ printf "%.1f", k/1024 }'; }
51
vmrss_kb() { awk '/^VmRSS:/{print $2}' "/proc/$1/status" 2>/dev/null; }
52
agg_vmrss(){ local p s=0 r; for p in "$@"; do r=$(vmrss_kb "$p"); [ -n "$r" ] && s=$((s+r)); done; echo "$s"; }
53
agg_smaps(){ # echoes "rss pss shclean privclean privdirty" (kB) summed over pids
54
   awk '
55
      /^Rss:/{r+=$2} /^Pss:/{p+=$2} /^Shared_Clean:/{s+=$2}
56
      /^Private_Clean:/{pc+=$2} /^Private_Dirty:/{d+=$2}
57
      END{ printf "%d %d %d %d %d", r, p, s, pc, d }' \
58
      $(for p in "$@"; do echo "/proc/$p/smaps_rollup"; done) 2>/dev/null
59
}
60

    
61
# Validate pids up front.
62
live=()
63
for p in "${pids[@]}"; do
64
   if [ -d "/proc/$p" ]; then live+=("$p"); else echo "warning: pid $p not present, skipping" >&2; fi
65
done
66
[ "${#live[@]}" -eq 0 ] && { echo "error: no live pids" >&2; exit 1; }
67

    
68
# Sample peak aggregate RSS over the settle window.
69
peak=0
70
iters=$(( settle * 5 )); [ "$iters" -lt 1 ] && iters=1
71
for ((i=0; i<iters; i++)); do
72
   agg="$(agg_vmrss "${live[@]}")"
73
   [ "${agg:-0}" -gt "$peak" ] && peak="$agg"
74
   [ "$settle" -gt 0 ] && sleep 0.2
75
done
76

    
77
# Re-collect live pids (some may have exited) for the smaps + archive snapshot.
78
snap=()
79
for p in "${live[@]}"; do [ -d "/proc/$p" ] && snap+=("$p"); done
80
rss=0 pss=0 shclean=0 privclean=0 privdirty=0 archive="-"
81
if [ "${#snap[@]}" -gt 0 ]; then
82
   read -r rss pss shclean privclean privdirty < <(agg_smaps "${snap[@]}")
83
   grep -q '/appcds/client/.*\.jsa' "/proc/${snap[0]}/maps" 2>/dev/null && archive="mapped" || archive="absent"
84
fi
85

    
86
# Persist the row, then reprint the accumulated table.
87
[ -n "$reset" ] && rm -f "$store"
88
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n' \
89
   "$label" "${#snap[@]}" "$peak" "$rss" "$pss" "$shclean" "$privclean" "$privdirty" "$archive" >> "$store"
90

    
91
echo "Sampled '$label' pids=[${pids[*]}] (live ${#snap[@]}) over ${settle}s; appended to $store"
92
echo
93
hdr='%-12s %4s %9s %9s %9s %10s %10s %10s  %-7s\n'
94
printf "$hdr" MODE "#Up" "PeakRSS" "Rss" "Pss" "SharedCln" "PrivClean" "PrivDirty" "ARCHIVE"
95
printf "$hdr" ""   ""    "(MB)"    "(MB)" "(MB)" "(MB)"      "(MB)"      "(MB)"      ""
96
while IFS=$'\t' read -r l n pk r p s pc pd a; do
97
   [ -z "$l" ] && continue
98
   printf "$hdr" "$l" "$n" "$(mb "$pk")" "$(mb "$r")" "$(mb "$p")" "$(mb "$s")" "$(mb "$pc")" "$(mb "$pd")" "$a"
99
done < "$store"
100

    
101
echo
102
echo "Notes:"
103
echo " - Rows accumulate across runs (server bounces between configs). Use --reset on"
104
echo "   the FIRST run of a fresh comparison to clear old rows."
105
echo " - ARCHIVE=mapped confirms the .jsa is actually in use for that webClient."
106
echo " - No ToSettle here: the process was already running, so startup time can't be"
107
echo "   measured after the fact (use appcds_mem.sh for the command-line client to get that)."