xiaohongshu
xiaohongshu
hammer-memory-profiler
Simplified memory profiling and leak detection for SDL3 HammerEngine using valgrind memcheck, AddressSanitizer, and massif. Identifies memory leaks, allocation hotspots, buffer reuse violations, and provides system-by-system memory breakdown with optimization suggestions. Use after performance-cr...
Full skill instructions
Comprehensive memory profiling and leak detection for SDL3 HammerEngine. Identifies memory leaks, per-frame allocation hotspots, buffer reuse violations, and provides actionable optimization recommendations following CLAUDE.md patterns.
This skill includes utility scripts in .claude/skills/hammer-memory-profiler/scripts/:
run_leak_check.sh - Quick memory leak detection with valgrind memcheckrun_massif_all_tests.sh - Run valgrind massif on all test executablesparse_massif.py - Parse massif reports and generate comprehensive analysisUse these scripts directly or let the skill invoke them automatically.
Memory management is critical for HammerEngine's performance targets (10K+ entities @ 60 FPS). This Skill automates:
Note: For thread safety validation (data races, deadlocks), use ThreadSanitizer instead of AddressSanitizer. See TSAN section below.
clear() vs reconstructionreserve() callsUse AskUserQuestion to determine profiling scope:
Question 1: Profiling Mode
Question 2: Test Scope
Question 3: Baseline Comparison
2a. Ensure Debug Build Exists
# Check if debug build exists
if [ ! -f "./bin/debug/thread_system_tests" ]; then
echo "Debug build not found. Building..."
cmake -B build/ -G Ninja -DCMAKE_BUILD_TYPE=Debug && ninja -C build
fi
2b. Run Valgrind Memcheck
Test Selection Based on Scope:
Core Tests Only:
TEST_EXECUTABLES=(
"./bin/debug/thread_system_tests"
"./bin/debug/buffer_utilization_tests"
"./bin/debug/event_manager_tests"
)
AI System:
TEST_EXECUTABLES=(
"./bin/debug/thread_safe_ai_manager_tests"
"./bin/debug/ai_optimization_tests"
"./bin/debug/behavior_functionality_tests"
)
Collision/Pathfinding:
TEST_EXECUTABLES=(
"./bin/debug/collision_system_tests"
"./bin/debug/pathfinder_manager_tests"
"./bin/debug/collision_pathfinding_integration_tests"
)
All Systems:
TEST_EXECUTABLES=(
# Run all test executables in bin/debug/
)
Valgrind Command Template:
OUTPUT_DIR="test_results/memory_profiles"
mkdir -p "$OUTPUT_DIR"
for TEST_EXEC in "${TEST_EXECUTABLES[@]}"; do
TEST_NAME=$(basename "$TEST_EXEC")
echo "Running valgrind on $TEST_NAME..."
valgrind \
--leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--verbose \
--log-file="$OUTPUT_DIR/${TEST_NAME}_memcheck.log" \
"$TEST_EXEC" --log_level=test_suite \
2>&1 | tee "$OUTPUT_DIR/${TEST_NAME}_output.txt"
done
Valgrind Flags Explained:
--leak-check=full: Detailed leak information--show-leak-kinds=all: Show all leak types (definite, indirect, possible, reachable)--track-origins=yes: Track origin of uninitialized values--verbose: Detailed output--log-file: Save valgrind output to file2c. Parse Valgrind Output
Extract Key Metrics:
# Parse all memcheck logs
for LOG in "$OUTPUT_DIR"/*_memcheck.log; do
TEST_NAME=$(basename "$LOG" _memcheck.log)
echo "=== $TEST_NAME ==="
# Definite leaks (CRITICAL)
DEFINITE_LEAKS=$(grep "definitely lost:" "$LOG" | tail -1 | awk '{print $4, $5}')
echo "Definite leaks: $DEFINITE_LEAKS"
# Indirect leaks
INDIRECT_LEAKS=$(grep "indirectly lost:" "$LOG" | tail -1 | awk '{print $4, $5}')
echo "Indirect leaks: $INDIRECT_LEAKS"
# Possible leaks
POSSIBLE_LEAKS=$(grep "possibly lost:" "$LOG" | tail -1 | awk '{print $4, $5}')
echo "Possible leaks: $POSSIBLE_LEAKS"
# Still reachable (not critical)
REACHABLE=$(grep "still reachable:" "$LOG" | tail -1 | awk '{print $4, $5}')
echo "Still reachable: $REACHABLE"
# Total heap usage
TOTAL_HEAP=$(grep "total heap usage:" "$LOG" | tail -1)
echo "Heap usage: $TOTAL_HEAP"
# Invalid reads/writes (CRITICAL)
INVALID_READ=$(grep -c "Invalid read" "$LOG")
INVALID_WRITE=$(grep -c "Invalid write" "$LOG")
echo "Invalid reads: $INVALID_READ"
echo "Invalid writes: $INVALID_WRITE"
echo ""
done
Severity Classification:
CRITICAL (Block merge):
WARNING (Review required):
INFO (Monitor):
2a. Build with AddressSanitizer
echo "Building with AddressSanitizer..."
# Clean build
rm -rf build/
# Configure with ASan
cmake -B build/ -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-D_GLIBCXX_DEBUG -fsanitize=address -fno-omit-frame-pointer -g" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DUSE_MOLD_LINKER=OFF
# Build
ninja -C build
Why AddressSanitizer for Allocation Profiling:
2b. Run Tests with ASan
OUTPUT_DIR="test_results/memory_profiles"
mkdir -p "$OUTPUT_DIR"
# Set ASan options
export ASAN_OPTIONS="detect_leaks=1:symbolize=1:log_path=$OUTPUT_DIR/asan"
for TEST_EXEC in "${TEST_EXECUTABLES[@]}"; do
TEST_NAME=$(basename "$TEST_EXEC")
echo "Running ASan on $TEST_NAME..."
"$TEST_EXEC" --log_level=test_suite 2>&1 | tee "$OUTPUT_DIR/${TEST_NAME}_asan_output.txt"
done
unset ASAN_OPTIONS
2c. Parse ASan Output
Look for allocation patterns:
for OUTPUT in "$OUTPUT_DIR"/*_asan_output.txt; do
TEST_NAME=$(basename "$OUTPUT" _asan_output.txt)
echo "=== $TEST_NAME ASan Analysis ==="
# Heap buffer overflow
BUFFER_OVERFLOW=$(grep -c "heap-buffer-overflow" "$OUTPUT")
if [ "$BUFFER_OVERFLOW" -gt 0 ]; then
echo "🔴 CRITICAL: $BUFFER_OVERFLOW heap buffer overflows detected"
grep -A 10 "heap-buffer-overflow" "$OUTPUT"
fi
# Use after free
USE_AFTER_FREE=$(grep -c "heap-use-after-free" "$OUTPUT")
if [ "$USE_AFTER_FREE" -gt 0 ]; then
echo "🔴 CRITICAL: $USE_AFTER_FREE use-after-free detected"
grep -A 10 "heap-use-after-free" "$OUTPUT"
fi
# Double free
DOUBLE_FREE=$(grep -c "attempting double-free" "$OUTPUT")
if [ "$DOUBLE_FREE" -gt 0 ]; then
echo "🔴 CRITICAL: $DOUBLE_FREE double-free detected"
grep -A 10 "attempting double-free" "$OUTPUT"
fi
# Allocation summary
grep "alloc-dealloc-mismatch" "$OUTPUT" || echo "✅ No alloc-dealloc mismatches"
echo ""
done
2d. Identify Per-Frame Allocation Hotspots
Search for hot-path allocations in code:
echo "=== Per-Frame Allocation Hotspot Analysis ==="
# Check for allocations in update loops
echo "Searching for potential per-frame allocations..."
# AIManager update loop
grep -n "std::vector" src/managers/AIManager.cpp | grep -i "update\|process" || echo "✅ AIManager: No obvious vector allocations in update"
# CollisionManager
grep -n "std::vector" src/managers/CollisionManager.cpp | grep -i "update\|detect" || echo "✅ CollisionManager: No obvious vector allocations in update"
# ParticleManager
grep -n "std::vector" src/managers/ParticleManager.cpp | grep -i "update\|render" || echo "✅ ParticleManager: No obvious vector allocations in update"
# Look for allocations inside loops
echo ""
echo "Checking for allocations inside loops (MAJOR ISSUE)..."
grep -A 5 "for\|while" src/managers/*.cpp | grep "std::vector\|std::make" | head -20
Per-Frame Allocation Patterns to Flag:
// 🔴 BAD: Allocates every frame
void update() {
std::vector<Data> buffer; // Fresh allocation
buffer.reserve(entityCount);
// ... use buffer
} // Deallocation
// 🔴 BAD: Allocation in loop
for (size_t i = 0; i < count; ++i) {
std::vector<Item> items; // Allocation per iteration!
// ...
}
// 🔴 BAD: No reserve before push_back loop
std::vector<Entity> entities;
for (...) {
entities.push_back(entity); // Incremental reallocations
}
Use ThreadSanitizer (TSAN) for:
Important: ThreadSanitizer and AddressSanitizer are mutually exclusive - use one or the other, not both.
2a. Build with ThreadSanitizer
echo "Building with ThreadSanitizer..."
# Clean build
rm -rf build/
# Configure with TSan
cmake -B build/ -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-D_GLIBCXX_DEBUG -fsanitize=thread -fno-omit-frame-pointer -g" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=thread" \
-DUSE_MOLD_LINKER=OFF
# Build
ninja -C build
Why ThreadSanitizer:
2b. Run Threading Tests with TSan
OUTPUT_DIR="test_results/memory_profiles"
mkdir -p "$OUTPUT_DIR"
# Threading-focused tests
THREAD_TESTS=(
"./bin/debug/thread_system_tests"
"./bin/debug/thread_safe_ai_manager_tests"
"./bin/debug/thread_safe_ai_integration_tests"
"./bin/debug/particle_manager_threading_tests"
"./bin/debug/event_coordination_integration_tests"
)
for TEST_EXEC in "${THREAD_TESTS[@]}"; do
TEST_NAME=$(basename "$TEST_EXEC")
echo "Running TSan on $TEST_NAME..."
"$TEST_EXEC" --log_level=test_suite 2>&1 | tee "$OUTPUT_DIR/${TEST_NAME}_tsan_output.txt"
done
2c. Parse TSan Output
Look for thread safety violations:
for OUTPUT in "$OUTPUT_DIR"/*_tsan_output.txt; do
TEST_NAME=$(basename "$OUTPUT" _tsan_output.txt)
echo "=== $TEST_NAME TSan Analysis ==="
# Data races
DATA_RACES=$(grep -c "WARNING: ThreadSanitizer: data race" "$OUTPUT")
if [ "$DATA_RACES" -gt 0 ]; then
echo "🔴 CRITICAL: $DATA_RACES data race(s) detected"
grep -A 15 "WARNING: ThreadSanitizer: data race" "$OUTPUT"
fi
# Deadlocks
DEADLOCKS=$(grep -c "WARNING: ThreadSanitizer: lock-order-inversion" "$OUTPUT")
if [ "$DEADLOCKS" -gt 0 ]; then
echo "🔴 CRITICAL: $DEADLOCKS potential deadlock(s) detected"
grep -A 15 "WARNING: ThreadSanitizer: lock-order-inversion" "$OUTPUT"
fi
# Thread leaks
THREAD_LEAKS=$(grep -c "WARNING: ThreadSanitizer: thread leak" "$OUTPUT")
if [ "$THREAD_LEAKS" -gt 0 ]; then
echo "⚠️ WARNING: $THREAD_LEAKS thread leak(s) detected"
fi
if [ "$DATA_RACES" -eq 0 ] && [ "$DEADLOCKS" -eq 0 ] && [ "$THREAD_LEAKS" -eq 0 ]; then
echo "✅ No thread safety issues detected"
fi
echo ""
done
Severity Classification:
CRITICAL (Block merge):
WARNING (Review required):
3a. Run Valgrind Massif
OUTPUT_DIR="test_results/memory_profiles"
mkdir -p "$OUTPUT_DIR"
for TEST_EXEC in "${TEST_EXECUTABLES[@]}"; do
TEST_NAME=$(basename "$TEST_EXEC")
echo "Running massif on $TEST_NAME..."
valgrind \
--tool=massif \
--massif-out-file="$OUTPUT_DIR/${TEST_NAME}_massif.out" \
--time-unit=ms \
--detailed-freq=1 \
--max-snapshots=100 \
--threshold=0.1 \
"$TEST_EXEC" --log_level=test_suite
done
Massif Flags:
--tool=massif: Heap profiler--time-unit=ms: Time in milliseconds--detailed-freq=1: Detailed snapshot frequency--max-snapshots=100: Store up to 100 snapshots--threshold=0.1: Capture 0.1% heap changes3b. Parse Massif Output
for MASSIF in "$OUTPUT_DIR"/*_massif.out; do
TEST_NAME=$(basename "$MASSIF" _massif.out)
echo "=== $TEST_NAME Massif Analysis ==="
# Generate text report
ms_print "$MASSIF" > "$OUTPUT_DIR/${TEST_NAME}_massif_report.txt"
# Extract peak memory
PEAK_MEM=$(grep "peak" "$MASSIF" | head -1)
echo "Peak memory: $PEAK_MEM"
# Extract allocation sites (top 10)
echo ""
echo "Top 10 allocation sites:"
ms_print "$MASSIF" | grep -A 1 "->.*%" | head -20
echo ""
done
3c. System-by-System Breakdown
Extract memory usage by manager:
echo "=== Memory Usage by System ==="
# Parse massif reports for specific managers
for REPORT in "$OUTPUT_DIR"/*_massif_report.txt; do
echo ""
echo "Report: $(basename "$REPORT")"
# AIManager allocations
AI_ALLOCS=$(grep -c "AIManager" "$REPORT")
echo " AIManager allocations: $AI_ALLOCS"
# CollisionManager allocations
COLLISION_ALLOCS=$(grep -c "CollisionManager" "$REPORT")
echo " CollisionManager allocations: $COLLISION_ALLOCS"
# PathfinderManager allocations
PATHFINDER_ALLOCS=$(grep -c "PathfinderManager" "$REPORT")
echo " PathfinderManager allocations: $PATHFINDER_ALLOCS"
# EventManager allocations
EVENT_ALLOCS=$(grep -c "EventManager" "$REPORT")
echo " EventManager allocations: $EVENT_ALLOCS"
# ParticleManager allocations
PARTICLE_ALLOCS=$(grep -c "ParticleManager" "$REPORT")
echo " ParticleManager allocations: $PARTICLE_ALLOCS"
done
4a. Scan for Buffer Reuse Patterns
Search for reusable buffers (member variables):
echo "=== Buffer Reuse Pattern Audit ==="
# Find all manager classes
MANAGERS=$(find include/managers -name "*.hpp" -type f)
for MANAGER in $MANAGERS; do
MANAGER_NAME=$(basename "$MANAGER" .hpp)
echo ""
echo "=== $MANAGER_NAME ==="
# Check for member vector variables (potential reuse buffers)
echo "Member vectors (should be reused):"
grep "std::vector" "$MANAGER" | grep "m_" | head -10
# Check implementation for clear() usage
CPP_FILE="src/managers/${MANAGER_NAME}.cpp"
if [ -f "$CPP_FILE" ]; then
CLEAR_COUNT=$(grep -c "\.clear()" "$CPP_FILE")
echo "clear() calls: $CLEAR_COUNT (good - reuses capacity)"
# Flag missing reserve() calls
RESERVE_COUNT=$(grep -c "\.reserve(" "$CPP_FILE")
echo "reserve() calls: $RESERVE_COUNT"
if [ "$RESERVE_COUNT" -eq 0 ]; then
echo "⚠️ WARNING: No reserve() calls found - check for incremental reallocations"
fi
fi
done
4b. Check for Buffer Reuse Anti-Patterns
Common anti-patterns:
echo ""
echo "=== Checking for Anti-Patterns ==="
# Anti-pattern 1: Local vectors in update functions
echo "1. Local vectors in update functions (should be members):"
grep -n "void.*update\|void.*process" src/managers/*.cpp | while read -r line; do
FILE=$(echo "$line" | cut -d: -f1)
LINE_NUM=$(echo "$line" | cut -d: -f2)
# Check 20 lines after function definition for std::vector
sed -n "${LINE_NUM},$((LINE_NUM+20))p" "$FILE" | grep -n "std::vector" | while read -r vec_line; do
echo " $FILE:$((LINE_NUM + $(echo "$vec_line" | cut -d: -f1))) - Local vector in update"
done
done
# Anti-pattern 2: Vector reconstruction instead of clear()
echo ""
echo "2. Vector reconstruction (use clear() instead):"
for CPP in src/managers/*.cpp; do
# Look for pattern: vectorName = std::vector<Type>();
grep -n "= std::vector<" "$CPP" | head -5
done
# Anti-pattern 3: Push_back without reserve
echo ""
echo "3. push_back loops without prior reserve():"
for CPP in src/managers/*.cpp; do
echo " Checking $CPP..."
# This is approximate - look for push_back in loops
grep -B 5 "push_back" "$CPP" | grep "for\|while" | head -3
done
4c. Verify CLAUDE.md Buffer Patterns
Good patterns from CLAUDE.md:
echo ""
echo "=== Verifying CLAUDE.md Buffer Patterns ==="
# Pattern 1: Member variables for hot-path buffers
echo "1. Checking for member buffer variables..."
for MANAGER in include/managers/*.hpp; do
MANAGER_NAME=$(basename "$MANAGER" .hpp)
MEMBER_BUFFERS=$(grep "m_.*Buffer\|m_.*Cache\|m_.*Results" "$MANAGER" | wc -l)
echo " $MANAGER_NAME: $MEMBER_BUFFERS reusable buffers"
done
# Pattern 2: clear() over reconstruction
echo ""
echo "2. Checking clear() usage (capacity preservation)..."
for CPP in src/managers/*.cpp; do
CLEAR_COUNT=$(grep -c "\.clear()" "$CPP")
RECONSTRUCT_COUNT=$(grep -c "= std::vector" "$CPP")
echo " $(basename "$CPP"): clear() = $CLEAR_COUNT, reconstruct = $RECONSTRUCT_COUNT"
if [ "$RECONSTRUCT_COUNT" -gt "$CLEAR_COUNT" ]; then
echo " ⚠️ More reconstructions than clears - check for capacity loss"
fi
done
# Pattern 3: reserve() before loops
echo ""
echo "3. Checking reserve() before insertion loops..."
for CPP in src/managers/*.cpp; do
echo " $(basename "$CPP"):"
grep -B 3 "for.*push_back\|while.*push_back" "$CPP" | grep "reserve(" || echo " ⚠️ No reserve() found before push_back loops"
done
3a. Load Baseline Metrics
BASELINE_DIR="test_results/memory_profiles/baseline"
if [ -d "$BASELINE_DIR" ] && [ "$COMPARE_BASELINE" = "Yes" ]; then
echo "=== Baseline Comparison ==="
# Compare leak counts
for LOG in "$OUTPUT_DIR"/*_memcheck.log; do
TEST_NAME=$(basename "$LOG" _memcheck.log)
BASELINE_LOG="$BASELINE_DIR/${TEST_NAME}_memcheck.log"
if [ -f "$BASELINE_LOG" ]; then
echo ""
echo "Test: $TEST_NAME"
# Current leaks
CURRENT_LEAKS=$(grep "definitely lost:" "$LOG" | tail -1 | awk '{print $4}')
CURRENT_LEAKS=${CURRENT_LEAKS:-0}
# Baseline leaks
BASELINE_LEAKS=$(grep "definitely lost:" "$BASELINE_LOG" | tail -1 | awk '{print $4}')
BASELINE_LEAKS=${BASELINE_LEAKS:-0}
# Compare
if [ "$CURRENT_LEAKS" -gt "$BASELINE_LEAKS" ]; then
DELTA=$((CURRENT_LEAKS - BASELINE_LEAKS))
echo " 🔴 REGRESSION: +$DELTA bytes leaked (was $BASELINE_LEAKS, now $CURRENT_LEAKS)"
elif [ "$CURRENT_LEAKS" -lt "$BASELINE_LEAKS" ]; then
DELTA=$((BASELINE_LEAKS - CURRENT_LEAKS))
echo " 🟢 IMPROVEMENT: -$DELTA bytes leaked (was $BASELINE_LEAKS, now $CURRENT_LEAKS)"
else
echo " ⚪ STABLE: $CURRENT_LEAKS bytes leaked (unchanged)"
fi
# Compare heap usage
CURRENT_HEAP=$(grep "total heap usage:" "$LOG" | tail -1 | awk '{print $5}')
BASELINE_HEAP=$(grep "total heap usage:" "$BASELINE_LOG" | tail -1 | awk '{print $5}')
if [ ! -z "$CURRENT_HEAP" ] && [ ! -z "$BASELINE_HEAP" ]; then
HEAP_DELTA=$((CURRENT_HEAP - BASELINE_HEAP))
echo " Total allocs: $CURRENT_HEAP (baseline: $BASELINE_HEAP, delta: $HEAP_DELTA)"
fi
fi
done
fi
3b. Save as New Baseline (if requested)
if [ "$BASELINE_MODE" = "Create new baseline" ]; then
echo ""
echo "=== Saving New Baseline ==="
mkdir -p "$BASELINE_DIR"
# Copy current results to baseline
cp "$OUTPUT_DIR"/*_memcheck.log "$BASELINE_DIR/" 2>/dev/null || true
cp "$OUTPUT_DIR"/*_massif.out "$BASELINE_DIR/" 2>/dev/null || true
# Save metadata
cat > "$BASELINE_DIR/baseline_metadata.txt" <<EOF
Baseline created: $(date)
Branch: $(git rev-parse --abbrev-ref HEAD)
Commit: $(git rev-parse HEAD)
Tests included: $(ls "$OUTPUT_DIR"/*_memcheck.log | wc -l)
EOF
echo "✅ Baseline saved to $BASELINE_DIR"
fi
Report Structure:
# HammerEngine Memory Profile Report
**Generated:** YYYY-MM-DD HH:MM:SS
**Branch:** <current-branch>
**Commit:** <commit-hash>
**Profiling Mode:** <mode>
**Test Scope:** <scope>
---
## Executive Summary
**Overall Status:** ✅ CLEAN / ⚠️ WARNINGS / 🔴 CRITICAL ISSUES
**Key Findings:**
- [Finding 1]
- [Finding 2]
- [Finding 3]
**Memory Health:** [Excellent/Good/Fair/Poor]
---
## Leak Detection Results
### Critical Leaks (BLOCKING)
| Test | Definite Leaks | Invalid Access | Status |
|------|----------------|----------------|--------|
| [Test 1] | [X bytes] | [N violations] | 🔴/✅ |
| [Test 2] | [X bytes] | [N violations] | 🔴/✅ |
**Total Definite Leaks:** [X bytes] (Target: 0 bytes)
### Leak Details
[For each test with leaks, include:]
**Test:** [test_name]
**Leak Location:** [file:line]
**Stack Trace:**
[valgrind stack trace]
**Likely Cause:** [Analysis]
**Suggested Fix:** [Specific code change]
---
## Allocation Profiling (if Mode 2)
### Per-Frame Allocation Hotspots
| System | Allocations/Frame | Impact | Status |
|--------|-------------------|--------|--------|
| AIManager | [N] | [Frame spike: Xms] | ⚠️/✅ |
| CollisionManager | [N] | [Frame spike: Xms] | ⚠️/✅ |
| ParticleManager | [N] | [Frame spike: Xms] | ⚠️/✅ |
**Total Per-Frame Allocations:** [N] (Target: 0 in hot paths)
### Anti-Pattern Violations
**1. Local Vectors in Update Functions:**
- `AIManager.cpp:123` - `std::vector<Data> localBuffer;` in `processBatch()`
- **Fix:** Make `m_processingBuffer` member variable, use `clear()` per frame
**2. Push_back Without Reserve:**
- `CollisionManager.cpp:456` - Loop with `results.push_back()` without `reserve()`
- **Fix:** Add `results.reserve(expectedCount);` before loop
**3. Vector Reconstruction:**
- `PathfinderManager.cpp:789` - `m_pathCache = std::vector<Path>();`
- **Fix:** Replace with `m_pathCache.clear();` to preserve capacity
---
## Memory Usage by System (if Mode 3)
### Peak Memory
| System | Peak Allocation | % of Total | Trend |
|--------|----------------|------------|-------|
| AIManager | [X MB] | [%] | 📈/📉/➡️ |
| CollisionManager | [X MB] | [%] | 📈/📉/➡️ |
| PathfinderManager | [X MB] | [%] | 📈/📉/➡️ |
| EventManager | [X MB] | [%] | 📈/📉/➡️ |
| ParticleManager | [X MB] | [%] | 📈/📉/➡️ |
| **Total** | **[X MB]** | **100%** | - |
### Top Allocation Sites
1. **AIManager::processBatch()** - [X MB] ([%] of total)
- [N] allocations
- Stack trace: [abbreviated]
2. **CollisionManager::detectCollisions()** - [X MB] ([%] of total)
- [N] allocations
- Stack trace: [abbreviated]
---
## Buffer Reuse Audit (if Mode 4)
### Pattern Compliance
| Manager | Member Buffers | clear() Usage | reserve() Usage | Grade |
|---------|----------------|---------------|-----------------|-------|
| AIManager | ✅ Yes | ✅ Correct | ✅ Present | A |
| CollisionManager | ✅ Yes | ⚠️ Partial | ❌ Missing | C |
| ParticleManager | ❌ No | ❌ Local vars | ❌ Missing | F |
**Overall Compliance:** [%] (Target: 100%)
### Recommendations
**AIManager:**
- ✅ Excellent buffer reuse pattern
- Document as reference implementation
**CollisionManager:**
- ⚠️ Add `reserve()` calls in `detectCollisions()` before `results.push_back()` loop
- Estimated improvement: -50 allocs/frame
**ParticleManager:**
- 🔴 Critical: Replace local `std::vector<Particle> activeParticles;` with member `m_activeParticles`
- 🔴 Add `m_activeParticles.clear()` at start of `update()`
- 🔴 Add `reserve(maxParticles)` in constructor
- Estimated improvement: -200 allocs/frame
---
## Baseline Comparison (if applicable)
### Leak Trend
| Test | Baseline | Current | Change | Status |
|------|----------|---------|--------|--------|
| [Test 1] | [X bytes] | [X bytes] | [+/-] | 🔴/🟢/⚪ |
### Memory Usage Trend
[Chart or table showing memory usage over time]
**Overall Trend:** [Improving/Stable/Degrading]
---
## Optimization Opportunities
### High Priority (Immediate Fix)
1. **ParticleManager: Eliminate per-frame allocations**
- **Current:** 200 allocs/frame (~128 KB/frame)
- **Impact:** Frame spikes of 5-10ms
- **Fix:** [Specific code changes]
- **Expected Improvement:** -5ms frame time
2. **CollisionManager: Add reserve() calls**
- **Current:** Incremental reallocations in query results
- **Impact:** 1-2ms overhead
- **Fix:** [Specific code changes]
- **Expected Improvement:** -1ms query time
### Medium Priority
3. **AIManager: Increase batch buffer size**
- **Current:** 1024 entities, reallocs when exceeded
- **Fix:** Increase to 2048 or make dynamic
- **Expected Improvement:** Eliminate rare reallocs
### Low Priority
4. **EventManager: Consider event pool**
- **Current:** Event objects allocated per dispatch
- **Fix:** Implement object pool for event reuse
- **Expected Improvement:** -10% event dispatch time
---
## Specific Code Fixes
### Fix 1: ParticleManager Buffer Reuse
**File:** `include/managers/ParticleManager.hpp:45`
**Before:**
```cpp
class ParticleManager
{
// ... no reusable buffer
};
After:
class ParticleManager
{
std::vector<Particle> m_activeParticles; // Reusable buffer
// ... reserve in constructor
};
File: src/managers/ParticleManager.cpp:123
Before:
void ParticleManager::update(float deltaTime)
{
std::vector<Particle> activeParticles; // Allocation every frame!
// ... use activeParticles
}
After:
void ParticleManager::update(float deltaTime)
{
m_activeParticles.clear(); // Reuse capacity
// ... use m_activeParticles
}
File: src/managers/ParticleManager.cpp:34 (constructor)
Add:
ParticleManager::ParticleManager()
{
m_activeParticles.reserve(MAX_PARTICLES); // Pre-allocate
}
Tests Run: [N] Tests Passed: [N] Critical Issues: [N] Warnings: [N]
Status: [✅ CLEAN / ⚠️ NEEDS REVIEW / 🔴 FIX REQUIRED]
Based on findings, these files should be modified:
include/managers/ParticleManager.hpp (add member buffer)
src/managers/ParticleManager.cpp (use buffer, add clear/reserve)
src/managers/CollisionManager.cpp (add reserve calls)
Re-run Profile:
# After fixes, re-run to verify
[Command to re-invoke this skill]
Report Generated By: hammer-memory-profiler Skill
Report Saved To: test_results/memory_profiles/memory_profile_YYYY-MM-DD.md
**Save report to:**
```bash
REPORT_FILE="test_results/memory_profiles/memory_profile_$(date +%Y-%m-%d_%H-%M-%S).md"
cat > "$REPORT_FILE" <<'EOF'
[Generated markdown report]
EOF
echo "✅ Memory profile report saved to: $REPORT_FILE"
Output to user:
=== HammerEngine Memory Profile ===
Mode: [Mode Name]
Scope: [Test Scope]
Duration: [Time taken]
Overall Status: [✅ CLEAN / ⚠️ WARNINGS / 🔴 CRITICAL]
Critical Issues: [N]
Warnings: [N]
[If critical:]
🔴 CRITICAL ISSUES FOUND - DO NOT COMMIT
- [Issue 1]
- [Issue 2]
[If warnings:]
⚠️ WARNINGS DETECTED - REVIEW RECOMMENDED
- [Warning 1]
- [Warning 2]
[If clean:]
✅ NO MEMORY ISSUES DETECTED
- 0 bytes leaked
- 0 invalid access violations
- Buffer reuse patterns correct
Memory Usage:
- Peak: [X MB]
- Total allocations: [N]
- Per-frame allocations: [N] (Target: 0)
Top Allocation Sites:
1. [System] - [X MB]
2. [System] - [X MB]
3. [System] - [X MB]
Baseline Comparison: [If applicable]
- Leaks: [+/-X bytes]
- Allocations: [+/-N]
- Trend: [Improving/Stable/Degrading]
Full Report: test_results/memory_profiles/memory_profile_YYYY-MM-DD.md
Next Steps:
[If critical] - Fix issues and re-run profile
[If clean] - Update baseline: "update memory baseline"
When the user says:
Activate this Skill automatically.
Use this Skill:
Symptom: Periodic frame drops every 1-2 seconds Cause: Heap allocations in update loop triggering OS paging Solution: Member buffer + clear() pattern from CLAUDE.md
Symptom: Gradual frame time increase with entity count Cause: Incremental vector reallocations (2x growth pattern) Solution: Pre-calculate size, call reserve() before loop
Symptom: "Still reachable" leaks from SDL Cause: Missing SDL_Destroy calls in destructors Solution: Ensure proper cleanup in manager destructors
Symptom: Allocation contention visible in profiler Cause: Multiple threads allocating from same heap Solution: Thread-local buffers or per-thread allocators
Symptom: High allocation rate despite buffer reuse Cause: Unnecessary shared_ptr copies (atomic ref-count ops) Solution: Use raw pointers in hot paths (see CLAUDE.md)
Manual Equivalent: 45-90 minutes per profiling session
Ready to profile HammerEngine memory usage. Ask user for profiling mode and scope.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience