Some checks failed
Music: - #random URL fragment shuffles playlist tracks before enqueuing - Lazy playlist resolution: first 10 tracks resolve immediately, remaining are fetched in a background task - !kept repair re-downloads kept tracks with missing local files - !kept shows [MISSING] marker for tracks without local files - TTS ducking: music ducks when merlin speaks via voice peer, smooth restore after TTS finishes Performance (from profiling): - Connection pool: preload_content=True for SOCKS connection reuse - Pool tuning: 30 pools / 8 connections (up from 20/4) - _PooledResponse wrapper for stdlib-compatible read interface - Iterative _extract_videos (replace 51K-deep recursion with stack) - proxy=False for local SearXNG Voice + multi-bot: - Per-bot voice config lookup ([<username>.voice] in TOML) - Mute detection: skip duck silence when all users muted - Autoplay shuffle deck (no repeats until full cycle) - Seek clamp to track duration (prevent seek-past-end stall) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
98 lines
2.5 KiB
Bash
Executable File
98 lines
2.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Analyze cProfile data from the bot process.
|
|
# Usage: tools/profile [OPTIONS] [FILE]
|
|
#
|
|
# Options:
|
|
# -n NUM Show top NUM entries (default: 30)
|
|
# -s SORT Sort by: cumtime, tottime, calls, name (default: cumtime)
|
|
# -f PATTERN Filter to entries matching PATTERN
|
|
# -c Callers view (who calls the hot functions)
|
|
# -h Show this help
|
|
#
|
|
# Examples:
|
|
# tools/profile # top 30 by cumulative time
|
|
# tools/profile -s tottime -n 20 # top 20 by total time
|
|
# tools/profile -f mumble # only mumble-related functions
|
|
# tools/profile -c -f stream_audio # who calls stream_audio
|
|
# tools/profile data/old.prof # analyze a specific file
|
|
|
|
# shellcheck source=tools/_common.sh
|
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
|
|
|
|
DEFAULT_PROF="$PROJECT_DIR/data/derp.prof"
|
|
TOP=30
|
|
SORT="cumtime"
|
|
PATTERN=""
|
|
CALLERS=false
|
|
|
|
usage() {
|
|
sed -n '2,/^$/s/^# \?//p' "$0"
|
|
exit 0
|
|
}
|
|
|
|
while getopts ":n:s:f:ch" opt; do
|
|
case $opt in
|
|
n) TOP="$OPTARG" ;;
|
|
s) SORT="$OPTARG" ;;
|
|
f) PATTERN="$OPTARG" ;;
|
|
c) CALLERS=true ;;
|
|
h) usage ;;
|
|
:) err "option -$OPTARG requires an argument"; exit 2 ;;
|
|
*) err "unknown option -$OPTARG"; exit 2 ;;
|
|
esac
|
|
done
|
|
shift $((OPTIND - 1))
|
|
|
|
PROF="${1:-$DEFAULT_PROF}"
|
|
|
|
if [[ ! -f "$PROF" ]]; then
|
|
err "profile not found: $PROF"
|
|
dim "run the bot with --cprofile and stop it gracefully"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate sort key
|
|
case "$SORT" in
|
|
cumtime|tottime|calls|name) ;;
|
|
*) err "invalid sort key: $SORT (use cumtime, tottime, calls, name)"; exit 2 ;;
|
|
esac
|
|
|
|
# Profile metadata
|
|
size=$(stat -c %s "$PROF" 2>/dev/null || stat -f %z "$PROF" 2>/dev/null)
|
|
human=$(numfmt --to=iec-i --suffix=B "$size" 2>/dev/null || echo "${size}B")
|
|
modified=$(stat -c %y "$PROF" 2>/dev/null | cut -d. -f1)
|
|
|
|
printf '%b%s%b\n' "$BLU" "Profile" "$RST"
|
|
dim "$PROF ($human, $modified)"
|
|
echo
|
|
|
|
# Build pstats script
|
|
read -r -d '' PYSCRIPT << 'PYEOF' || true
|
|
import pstats
|
|
import sys
|
|
import io
|
|
|
|
prof_path = sys.argv[1]
|
|
sort_key = sys.argv[2]
|
|
top_n = int(sys.argv[3])
|
|
pattern = sys.argv[4]
|
|
callers = sys.argv[5] == "1"
|
|
|
|
p = pstats.Stats(prof_path, stream=sys.stdout)
|
|
p.strip_dirs()
|
|
p.sort_stats(sort_key)
|
|
|
|
if pattern:
|
|
if callers:
|
|
p.print_callers(pattern, top_n)
|
|
else:
|
|
p.print_stats(pattern, top_n)
|
|
else:
|
|
if callers:
|
|
p.print_callers(top_n)
|
|
else:
|
|
p.print_stats(top_n)
|
|
PYEOF
|
|
|
|
exec python3 -c "$PYSCRIPT" "$PROF" "$SORT" "$TOP" "$PATTERN" "$( $CALLERS && echo 1 || echo 0 )"
|