summaryrefslogtreecommitdiffstats
path: root/tools/guest-diag/pin_supervisor.sh
blob: 5bbab0cb38eca8efcc05cb3d78ada335020d7a6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) 2026 Open-IOV contributors
# pin_supervisor.sh - supervise the host-side pin tools (pin_gr_stream.py and
# semfix_uvmt.py) so a silent death cannot strand every later CUDA client at
# first cudaMalloc.
#
# Both tools write a heartbeat file; this script checks, every 5 s, that each
# process is alive (exact script path) and that its heartbeat mtime is < 30 s.
# On failure it kills the tool (exact path match) and restarts it, appending
# RESTART lines to the supervisor log. After 10 restarts in one hour for a
# tool it logs GIVEUP and exits 1. Only one semfix_uvmt instance is ever
# started (QEMU's QMP socket accepts a single client).
#
# Usage: pin_supervisor.sh LOGDIR TAG
# Logs: LOGDIR/12-pin-supervisor-TAG.log (supervisor)
#       LOGDIR/12-pin_gr_stream-TAG.log, LOGDIR/12-semfix-TAG.log (tools)
set -uo pipefail

LOGDIR="${1:?usage: pin_supervisor.sh LOGDIR TAG}"
TAG="${2:?usage: pin_supervisor.sh LOGDIR TAG}"

HERE=$(cd "$(dirname "$0")" && pwd)
PIN="$HERE/pin_gr_stream.py"
SEMFIX="$HERE/semfix_uvmt.py"
PIN_HB="${PIN_GR_HEARTBEAT:-/tmp/opencode/pin_gr_heartbeat}"
SEMFIX_HB="${SEMFIX_HEARTBEAT_FILE:-/tmp/opencode/semfix_heartbeat}"
SUPLOG="$LOGDIR/12-pin-supervisor-$TAG.log"
PIN_LOG="$LOGDIR/12-pin_gr_stream-$TAG.log"
SEMFIX_LOG="$LOGDIR/12-semfix-$TAG.log"
WINDOW=3600
CAP=10
mkdir -p "$LOGDIR" /tmp/opencode

log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$SUPLOG"; }

tool_alive() { pgrep -f "python3 $1" >/dev/null 2>&1; }

hb_age() {  # age in seconds of the heartbeat file, 9999 if missing
    local f="$1" m
    m=$(stat -c %Y "$f" 2>/dev/null) || { echo 9999; return; }
    echo $(( $(date +%s) - m ))
}

kill_tool() {
    pkill -f "python3 $1" 2>/dev/null
    sleep 1
    if pgrep -f "python3 $1" >/dev/null 2>&1; then
        pkill -9 -f "python3 $1" 2>/dev/null
        sleep 1
    fi
    return 0
}

start_pin() {
    if tool_alive "$PIN"; then return 0; fi
    touch "$PIN_HB" 2>/dev/null || true
    ( setsid nohup env SEMFIX_COUNTER_RETRY=10 \
        python3 "$PIN" >> "$PIN_LOG" 2>&1 & )
    log "START pin_gr_stream"
}

start_semfix() {
    # pgrep exact-count check before start: if an instance already exists
    # (ours or a leftover), never start a second one.
    if tool_alive "$SEMFIX"; then
        log "semfix already running - not starting another"
        return 0
    fi
    touch "$SEMFIX_HB" 2>/dev/null || true
    ( setsid nohup env SEMFIX_SKIP_PB=0 SEMFIX_COUNTER_RETRY=10 \
        python3 "$SEMFIX" 0.5 >> "$SEMFIX_LOG" 2>&1 & )
    log "START semfix_uvmt"
}

prune() {  # $1 now, $2 list -> list with entries older than WINDOW dropped
    local now="$1" t out=""
    for t in $2; do
        [ $(( now - t )) -lt "$WINDOW" ] && out="$out $t"
    done
    printf '%s' "$out"
}

count_list() {  # $1 list -> element count
    local n=0 t
    for t in $1; do n=$(( n + 1 )); done
    echo "$n"
}

restarts_pin=""
restarts_semfix=""

restart() {  # $1 label, $2 script path, $3 start function
    local now list n
    now=$(date +%s)
    if [ "$1" = pin_gr_stream ]; then
        restarts_pin=$(prune "$now" "$restarts_pin")
        n=$(count_list "$restarts_pin")
    else
        restarts_semfix=$(prune "$now" "$restarts_semfix")
        n=$(count_list "$restarts_semfix")
    fi
    if [ "$n" -ge "$CAP" ]; then
        log "GIVEUP $1: $n restarts within ${WINDOW}s"
        exit 1
    fi
    kill_tool "$2"
    if [ "$1" = pin_gr_stream ]; then
        restarts_pin="$restarts_pin $now"
    else
        restarts_semfix="$restarts_semfix $now"
    fi
    log "RESTART $(( n + 1 )) $1 $(date +%Y-%m-%dT%H:%M:%S)"
    "$3"
}

check_tool() {  # $1 label, $2 path, $3 heartbeat file, $4 start function
    local age
    age=$(hb_age "$3")
    if tool_alive "$2" && [ "$age" -le 30 ]; then
        return 0
    fi
    if ! tool_alive "$2"; then
        log "$1 not running (heartbeat age ${age}s) - restarting"
    else
        log "$1 heartbeat stale (${age}s) - restarting"
    fi
    restart "$1" "$2" "$4"
}

log "pin_supervisor up (LOGDIR=$LOGDIR TAG=$TAG)"
start_pin
start_semfix
while true; do
    sleep 5
    check_tool pin_gr_stream "$PIN" "$PIN_HB" start_pin
    check_tool semfix_uvmt "$SEMFIX" "$SEMFIX_HB" start_semfix
done