#!/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 )"