| #!/bin/bash |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may |
| # not use this file except in compliance with the License. You may obtain |
| # a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations |
| # under the License. |
| |
| set -o errexit |
| |
| # TODO(frickler): make this use stackrc variables |
| if [ -x /opt/stack/data/venv/bin/python ]; then |
| PYTHON=/opt/stack/data/venv/bin/python |
| else |
| PYTHON=${PYTHON:-python3} |
| fi |
| |
| # time to sleep between checks |
| SLEEP_TIME=20 |
| |
| # MemAvailable is the best estimation and has built-in heuristics |
| # around reclaimable memory. However, it is not available until 3.14 |
| # kernel (i.e. Ubuntu LTS Trusty misses it). In that case, we fall |
| # back to free+buffers+cache as the available memory. |
| USE_MEM_AVAILABLE=0 |
| if grep -q '^MemAvailable:' /proc/meminfo; then |
| USE_MEM_AVAILABLE=1 |
| fi |
| |
| function get_mem_unevictable { |
| awk '/^Unevictable:/ {print $2}' /proc/meminfo |
| } |
| |
| function get_mem_available { |
| if [[ $USE_MEM_AVAILABLE -eq 1 ]]; then |
| awk '/^MemAvailable:/ {print $2}' /proc/meminfo |
| else |
| awk '/^MemFree:/ {free=$2} |
| /^Buffers:/ {buffers=$2} |
| /^Cached:/ {cached=$2} |
| END { print free+buffers+cached }' /proc/meminfo |
| fi |
| } |
| |
| function tracker { |
| local low_point |
| local unevictable_point |
| low_point=$(get_mem_available) |
| # log mlocked memory at least on first iteration |
| unevictable_point=0 |
| while [ 1 ]; do |
| |
| local mem_available |
| mem_available=$(get_mem_available) |
| |
| local unevictable |
| unevictable=$(get_mem_unevictable) |
| |
| if [ $mem_available -lt $low_point -o $unevictable -ne $unevictable_point ]; then |
| echo "[[[" |
| date |
| |
| # whenever we see less memory available than last time, dump the |
| # snapshot of current usage; i.e. checking the latest entry in the file |
| # will give the peak-memory usage |
| if [[ $mem_available -lt $low_point ]]; then |
| low_point=$mem_available |
| echo "---" |
| # always available greppable output; given difference in |
| # meminfo output as described above... |
| echo "memory_tracker low_point: $mem_available" |
| echo "---" |
| cat /proc/meminfo |
| echo "---" |
| # would hierarchial view be more useful (-H)? output is |
| # not sorted by usage then, however, and the first |
| # question is "what's using up the memory" |
| # |
| # there are a lot of kernel threads, especially on a 8-cpu |
| # system. do a best-effort removal to improve |
| # signal/noise ratio of output. |
| ps --sort=-pmem -eo pid:10,pmem:6,rss:15,ppid:10,cputime:10,nlwp:8,wchan:25,args:100 | |
| grep -v ']$' |
| fi |
| echo "---" |
| |
| # list processes that lock memory from swap |
| if [[ $unevictable -ne $unevictable_point ]]; then |
| unevictable_point=$unevictable |
| ${PYTHON} $(dirname $0)/mlock_report.py |
| fi |
| |
| echo "]]]" |
| fi |
| sleep $SLEEP_TIME |
| done |
| } |
| |
| function usage { |
| echo "Usage: $0 [-x] [-s N]" 1>&2 |
| exit 1 |
| } |
| |
| while getopts ":s:x" opt; do |
| case $opt in |
| s) |
| SLEEP_TIME=$OPTARG |
| ;; |
| x) |
| set -o xtrace |
| ;; |
| *) |
| usage |
| ;; |
| esac |
| done |
| shift $((OPTIND-1)) |
| |
| tracker |