ctdb-scripts: Split CTDB configuration loading
[samba.git] / ctdb / config / debug_locks.sh
1 #!/bin/sh
2
3 # This script parses /proc/locks and finds the processes that are holding
4 # locks on CTDB databases.  For all those processes the script dumps a
5 # stack trace.
6 #
7 # This script can be used only if Samba is configured to use fcntl locks
8 # rather than mutex locks.
9
10 [ -n "$CTDB_BASE" ] || \
11     CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; echo "$PWD")
12
13 . "${CTDB_BASE}/functions"
14
15 # Default fallback location for database directories.
16 # These can be overwritten from CTDB configuration
17 CTDB_DBDIR="${CTDB_VARDIR}"
18 CTDB_DBDIR_PERSISTENT="${CTDB_VARDIR}/persistent"
19
20 loadconfig
21
22 (
23     flock -n 9 || exit 1
24
25     echo "===== Start of debug locks PID=$$ ====="
26
27     # Create sed expression to convert inodes to names.
28     # Filenames don't contain dashes and we want basenames
29     # shellcheck disable=SC2035
30     sed_cmd=$(cd "$CTDB_DBDIR" &&
31                   stat -c "s#[0-9a-f]*:[0-9a-f]*:%i #%n #" *.tdb.* 2>/dev/null ;
32               cd "$CTDB_DBDIR_PERSISTENT" &&
33                   stat -c "s#[0-9a-f]*:[0-9a-f]*:%i #%n #" *.tdb.* 2>/dev/null)
34
35     # Parse /proc/locks and extract following information
36     #    pid process_name tdb_name offsets [W]
37     out=$( grep -F "POSIX  ADVISORY  WRITE" /proc/locks |
38     awk '{ if($2 == "->") { print $6, $7, $8, $9, "W" } else { print $5, $6, $7, $8 } }' |
39     while read pid rest ; do
40         pname=$(readlink "/proc/${pid}/exe")
41         echo "$pid $pname $rest"
42     done | sed -e "$sed_cmd" | grep "\.tdb" )
43
44     if [ -n "$out" ]; then
45         # Log information about locks
46         echo "$out"
47
48         # Find processes that are waiting for locks
49         dbs=$(echo "$out" | grep "W$" | awk '{print $3}')
50         all_pids=""
51         for db in $dbs ; do
52             pids=$(echo "$out" | grep -v "W$" | grep "$db" | grep -v ctdbd | awk '{print $1}')
53             all_pids="$all_pids $pids"
54         done
55         # Use word splitting to squash whitespace
56         # shellcheck disable=SC2086
57         pids=$(echo $all_pids | tr " " "\n" | sort -u)
58
59         # For each process waiting, log stack trace
60         for pid in $pids ; do
61             echo "----- Stack trace for PID=$pid -----"
62             # x is intentionally ignored
63             # shellcheck disable=SC2034
64             read x x state x <"/proc/${pid}/stat"
65             if [ "$state" = "D" ] ; then
66                 # Don't run gstack on a process in D state since
67                 # gstack will hang until the process exits D state.
68                 # Although it is possible for a process to transition
69                 # to D state after this check, it is unlikely because
70                 # if a process is stuck in D state then it is probably
71                 # the reason why this script was called.  Note that a
72                 # kernel stack almost certainly won't help diagnose a
73                 # deadlock... but it will probably give us someone to
74                 # blame!
75                 echo "----- Process in D state, printing kernel stack only"
76                 cat "/proc/${pid}/stack"
77             else
78                 gstack "$pid"
79                 # gcore -o /var/log/core-deadlock-ctdb $pid
80             fi
81         done
82     fi
83
84     echo "===== End of debug locks PID=$$ ====="
85 )9>"${CTDB_SCRIPT_VARDIR}/debug_locks.lock" | script_log "ctdbd-lock"
86
87 exit 0