Files
derp/tools/profile
user 6083de13f9
Some checks failed
CI / gitleaks (push) Failing after 3s
CI / lint (push) Successful in 22s
CI / test (3.11) (push) Failing after 2m47s
CI / test (3.13) (push) Failing after 2m52s
CI / test (3.12) (push) Failing after 2m54s
CI / build (push) Has been skipped
feat: playlist shuffle, lazy resolution, TTS ducking, kept repair
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>
2026-02-22 16:21:47 +01:00

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