#!/bin/bash # analyse_instruments.sh # Usage: ./analyse_instruments.sh instruments_output_20260513_212052 set -uo pipefail OUTPUT_DIR="${1:?Usage: $0 }" if [[ ! -d "$OUTPUT_DIR" ]]; then echo "❌ Directory not found: $OUTPUT_DIR" exit 1 fi echo "" echo "═══════════════════════════════════════════════════════════════" echo "INSTRUMENTS SESSION ANALYSIS" echo "Directory: $OUTPUT_DIR" echo "Analysed: $(date)" echo "═══════════════════════════════════════════════════════════════" echo "" # ── Session summary ─────────────────────────────────────────────────────── SUMMARY="${OUTPUT_DIR}/00_summary.txt" if [[ -f "$SUMMARY" ]]; then echo "── Session Summary ──────────────────────────────────────────────" cat "$SUMMARY" echo "" fi # ── Per-template results ────────────────────────────────────────────────── echo "── Template Results ─────────────────────────────────────────────" printf " %-30s %-6s %-8s %s\n" "Template" "Status" "Elapsed" "Trace" printf " %-30s %-6s %-8s %s\n" \ "──────────────────────────────" "──────" "────────" "─────" PASS_COUNT=0 SKIP_COUNT=0 FAIL_COUNT=0 MISSING_COUNT=0 for log in "${OUTPUT_DIR}"/*.txt; do # Skip the summary file and load tester logs [[ "$log" == *"00_summary"* ]] && continue [[ "$log" == *"_load_tester"* ]] && continue [[ -f "$log" ]] || continue name=$(basename "$log" .txt | tr '_' ' ') # Extract status and elapsed — handle both old and new log formats status=$(grep "^Status:" "$log" 2>/dev/null | tail -1 | sed 's/^Status:[ ]*//') elapsed=$(grep "^Elapsed:" "$log" 2>/dev/null | tail -1 | sed 's/^Elapsed:[ ]*//') # Fallback if fields are empty [[ -z "$status" ]] && status="unknown" [[ -z "$elapsed" ]] && elapsed="?" # Check if a trace file was copied for this template SAFE=$(echo "$name" | tr ' ' '_') if [[ -d "${OUTPUT_DIR}/${SAFE}.trace" ]]; then trace_status="✅ yes" else trace_status="❌ no" MISSING_COUNT=$((MISSING_COUNT + 1)) fi icon="✅" case "$status" in PASS) PASS_COUNT=$((PASS_COUNT + 1)) ;; SKIP) SKIP_COUNT=$((SKIP_COUNT + 1)); icon="⚠️ " ;; FAIL) FAIL_COUNT=$((FAIL_COUNT + 1)); icon="❌" ;; *) icon="❓" ;; esac printf " %-30s %s %-4s %-8s %s\n" \ "$name" "$icon" "$status" "$elapsed" "$trace_status" done echo "" echo " Pass: $PASS_COUNT Skip: $SKIP_COUNT Fail: $FAIL_COUNT Missing traces: $MISSING_COUNT" echo "" # ── Trace files ─────────────────────────────────────────────────────────── echo "── Trace Files in Output Directory ─────────────────────────────" TRACE_COUNT=0 while IFS= read -r -d '' trace; do size=$(du -sh "$trace" 2>/dev/null | awk '{print $1}') name=$(basename "$trace") printf " %-40s %s\n" "$name" "$size" TRACE_COUNT=$((TRACE_COUNT + 1)) done < <(find "$OUTPUT_DIR" -name "*.trace" -print0 2>/dev/null) if [[ $TRACE_COUNT -eq 0 ]]; then echo " No .trace files found in $OUTPUT_DIR" echo "" echo " Searching common locations for recent traces…" FOUND_ELSEWHERE=false for search_dir in \ "${HOME}/Library/Instruments" \ "${HOME}/Documents/Instruments" \ "target/instruments"; do if [[ -d "$search_dir" ]]; then COUNT=$(find "$search_dir" -name "*.trace" -maxdepth 3 2>/dev/null | wc -l | tr -d ' ') if [[ "$COUNT" -gt 0 ]]; then echo " Found $COUNT trace(s) in: $search_dir" find "$search_dir" -name "*.trace" -maxdepth 3 2>/dev/null \ | sort -r | head -5 | sed 's/^/ /' FOUND_ELSEWHERE=true fi fi done if [[ "$FOUND_ELSEWHERE" == "true" ]]; then echo "" echo " To copy them to the output directory:" echo " cp -r ~/Library/Instruments/*.trace ${OUTPUT_DIR}/" fi fi echo "" # ── Load tester output during each template ─────────────────────────────── LOAD_LOGS=("${OUTPUT_DIR}"/*_load_tester.txt) # Check if glob expanded (bash 3.2 compatible way) if [[ -f "${LOAD_LOGS[0]}" ]]; then echo "── Load Tester Output Per Template ─────────────────────────────" for load_log in "${LOAD_LOGS[@]}"; do [[ -f "$load_log" ]] || continue template_name=$(basename "$load_log" _load_tester.txt | tr '_' ' ') echo "" echo " During: $template_name" echo " $(printf '─%.0s' {1..50})" if grep -q "CONCLUSION" "$load_log" 2>/dev/null; then # Load tester completed — show the summary table grep -A 30 "PROGRESSIVE LOAD REPORT" "$load_log" 2>/dev/null \ | head -30 | sed 's/^/ /' elif grep -q "connected" "$load_log" 2>/dev/null; then # Partial run — show last meaningful output echo " (Partial run — load tester did not complete all profiles)" grep "connected\|registered\|PASS\|FAIL\|Duration" "$load_log" \ 2>/dev/null | tail -10 | sed 's/^/ /' else echo " (Load tester did not reach the server during this capture)" tail -5 "$load_log" | sed 's/^/ /' fi done echo "" fi # ── What to open ───────────────────────────────────────────────────────── echo "── Next Steps ───────────────────────────────────────────────────" echo "" if [[ $TRACE_COUNT -gt 0 ]]; then echo " Open all traces in Instruments.app:" echo " open ${OUTPUT_DIR}/*.trace" echo "" echo " Priority order:" for priority_name in \ "Time_Profiler" \ "Locks" \ "Thread_States" \ "Allocations" \ "Leaks" \ "Network"; do trace="${OUTPUT_DIR}/${priority_name}.trace" if [[ -d "$trace" ]]; then echo " open \"${trace}\"" fi done else echo " ⚠️ No traces in output directory." echo " Run the profiling script again, or copy traces manually." echo "" echo " Quick re-run of the most important templates:" echo " ./run_instruments.sh --template \"Time Profiler\" --time-limit 90" echo " ./run_instruments.sh --template \"Locks\" --time-limit 90" echo " ./run_instruments.sh --template \"Allocations\" --time-limit 90" fi echo "" echo " Re-run a single template:" echo " ./run_instruments.sh --template \"Time Profiler\" --time-limit 120" echo "" echo " Re-run everything:" echo " pkill $BINARY 2>/dev/null || true" echo " ./run_instruments.sh" echo ""