|
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)."
|