Files
StreamLens/interactive_debug.py

277 lines
10 KiB
Python

#!/usr/bin/env python3
"""
Interactive debugging for StreamLens button issues
Run this and interact with the live app while monitoring button states
"""
import sys
import time
import threading
from pathlib import Path
# Add analyzer to path
sys.path.insert(0, str(Path(__file__).parent))
from analyzer.tui.textual.app_v2 import StreamLensAppV2
from analyzer.analysis.core import EthernetAnalyzer
from textual_state_visualizer import TextualStateMonitor, TextualStateWebServer
class InteractiveDebugger:
"""Interactive debugger that monitors button states in real-time"""
def __init__(self):
self.analyzer = None
self.app = None
self.monitor = None
self.web_server = None
self.monitoring = False
def setup_analyzer_with_data(self):
"""Create analyzer with some sample data"""
from analyzer.models.flow_stats import FlowStats, FrameTypeStats
self.analyzer = EthernetAnalyzer()
# Add sample flows to trigger button updates
flow1 = FlowStats(src_ip="192.168.1.1", dst_ip="192.168.1.2")
flow1.frame_types["CH10-Data"] = FrameTypeStats("CH10-Data", count=1500)
flow1.frame_types["UDP"] = FrameTypeStats("UDP", count=800)
self.analyzer.flows["flow1"] = flow1
flow2 = FlowStats(src_ip="192.168.1.3", dst_ip="192.168.1.4")
flow2.frame_types["PTP-Sync"] = FrameTypeStats("PTP-Sync", count=600)
flow2.frame_types["PTP-Signaling"] = FrameTypeStats("PTP-Signaling", count=300)
self.analyzer.flows["flow2"] = flow2
print("📊 Sample data added to analyzer:")
print(" Flow 1: CH10-Data(1500), UDP(800)")
print(" Flow 2: PTP-Sync(600), PTP-Signaling(300)")
def start_debugging(self):
"""Start the app with full debugging enabled"""
print("🚀 Starting StreamLens with interactive debugging...")
# Setup analyzer
self.setup_analyzer_with_data()
# Create app
self.app = StreamLensAppV2(analyzer=self.analyzer)
# Start state monitoring
self.monitor = TextualStateMonitor(self.app)
self.monitor.start_monitoring(interval=0.5) # Monitor every 500ms
# Start web interface
self.web_server = TextualStateWebServer(self.monitor, port=8080)
self.web_server.start()
print("🌐 Web debugging interface: http://localhost:8080")
print("📱 Starting StreamLens app...")
print()
print("🔧 DEBUGGING COMMANDS (while app is running):")
print(" Ctrl+D,T - Print widget tree to console")
print(" Ctrl+D,F - Show focused widget")
print(" Ctrl+D,W - Start additional web debugger")
print()
print("🧪 INTERACTIVE TESTING:")
print(" 1. Watch the web interface in your browser")
print(" 2. Try loading a PCAP file in the app")
print(" 3. Watch for button changes in real-time")
print(" 4. Use keyboard shortcuts to debug instantly")
print()
print("📊 The web interface will show:")
print(" - Real-time widget tree")
print(" - Button count and properties")
print(" - State changes as they happen")
print(" - Focus tracking")
print()
# Run the app
try:
self.app.run()
except KeyboardInterrupt:
print("\n🛑 App stopped by user")
finally:
self.cleanup()
def cleanup(self):
"""Clean up debugging resources"""
print("\n🧹 Cleaning up debugging...")
if self.monitor:
self.monitor.stop_monitoring()
if self.web_server:
self.web_server.stop()
def create_vscode_debug_config():
"""Create VS Code debug configuration"""
vscode_config = {
"version": "0.2.0",
"configurations": [
{
"name": "Debug StreamLens Interactive",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/interactive_debug.py",
"console": "integratedTerminal",
"justMyCode": False,
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
},
{
"name": "Debug StreamLens Button Issues",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/debug_button_issues.py",
"console": "integratedTerminal",
"justMyCode": False,
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
]
}
# Create .vscode directory if it doesn't exist
vscode_dir = Path(".vscode")
vscode_dir.mkdir(exist_ok=True)
# Write launch.json
import json
launch_json = vscode_dir / "launch.json"
with open(launch_json, 'w') as f:
json.dump(vscode_config, f, indent=4)
print(f"✅ Created VS Code debug configuration: {launch_json}")
def create_breakpoint_helper():
"""Create a helper script with strategic breakpoints"""
breakpoint_script = '''
# Add these breakpoints to key locations for debugging
# 1. In filtered_flow_view.py - refresh_frame_types method
def refresh_frame_types(self):
"""Update frame type button counts and reorder by count (highest to left)"""
import pdb; pdb.set_trace() # BREAKPOINT: Start of refresh
# Throttle button refresh to prevent race conditions
import time
current_time = time.time()
if current_time - self._last_refresh_time < self._refresh_throttle_seconds:
print(f"🚫 Refresh throttled (last: {self._last_refresh_time}, current: {current_time})")
return # Skip refresh if called too recently
print(f"🔄 Starting refresh_frame_types at {current_time}")
self._last_refresh_time = current_time
# Get all detected frame types with their total packet counts
frame_types = self._get_all_frame_types()
print(f"📊 Frame types found: {frame_types}")
# If no frame types yet, skip button update
if not frame_types:
print("⚠️ No frame types, skipping button update")
return
# BREAKPOINT: Before button removal/creation
import pdb; pdb.set_trace()
# Rest of method...
# 2. In filtered_flow_view.py - compose method
def compose(self):
"""Create the filter bar and flow grid"""
import pdb; pdb.set_trace() # BREAKPOINT: Widget creation
# Filter button bar at top
with Horizontal(id="filter-bar"):
# Overview button (hotkey 1) - compact format
overview_btn = Button("1.Overview", id="btn-overview", classes="-active")
self.frame_type_buttons["Overview"] = overview_btn
print(f"✅ Created overview button: {overview_btn}")
yield overview_btn
# Create predefined frame type buttons at initialization
hotkeys = ['2', '3', '4', '5', '6', '7', '8', '9', '0']
for i, frame_type in enumerate(self.predefined_frame_types):
if i < len(hotkeys):
# Start with 0 count - will be updated during data refresh
btn = FrameTypeButton(frame_type, hotkeys[i], 0)
self.frame_type_buttons[frame_type] = btn
print(f"✅ Created predefined button {i+1}: {btn} for {frame_type}")
yield btn
# BREAKPOINT: After all buttons created
import pdb; pdb.set_trace()
# 3. Strategic print statements for tracking
def debug_button_lifecycle():
"""Add this to track button lifecycle"""
def log_button_state(self, phase):
print(f"\\n🔍 BUTTON STATE - {phase}:")
print(f" Buttons dict: {len(self.frame_type_buttons)} entries")
for name, btn in self.frame_type_buttons.items():
if hasattr(btn, 'parent'):
parent_status = "has parent" if btn.parent else "NO PARENT"
else:
parent_status = "no parent attr"
print(f" {name}: {btn} ({parent_status})")
'''
with open("debugging_breakpoints.py", 'w') as f:
f.write(breakpoint_script)
print("✅ Created debugging_breakpoints.py with strategic breakpoint locations")
def main():
print("🔧 StreamLens Interactive Debugging Setup")
print("=" * 60)
# Create VS Code configuration
create_vscode_debug_config()
# Create breakpoint helper
create_breakpoint_helper()
print("\n🎯 RECOMMENDED DEBUGGING APPROACH:")
print("\n1. **VS Code Debugging**:")
print(" - Open this project in VS Code")
print(" - Use F5 to start 'Debug StreamLens Interactive'")
print(" - Set breakpoints in filtered_flow_view.py")
print(" - Step through button creation/refresh logic")
print("\n2. **Interactive Monitoring**:")
print(" - Run: python interactive_debug.py")
print(" - Open http://localhost:8080 in browser")
print(" - Load a PCAP file and watch real-time changes")
print("\n3. **Strategic Breakpoints**:")
print(" - Add breakpoints at:")
print(" • filtered_flow_view.py:142 (compose method)")
print(" • filtered_flow_view.py:219 (refresh_frame_types method)")
print(" • filtered_flow_view.py:171 (on_mount method)")
print("\n4. **Live Console Debugging**:")
print(" - While app runs, press Ctrl+D,T for widget tree")
print(" - Check button parent relationships")
print(" - Monitor refresh timing")
print("\n🔍 KEY THINGS TO CHECK:")
print(" ✓ Are buttons created in compose()?")
print(" ✓ Do buttons have parents after creation?")
print(" ✓ What triggers refresh_frame_types()?")
print(" ✓ Are buttons removed during refresh?")
print(" ✓ What's the order of operations?")
choice = input("\n❓ Start interactive debugging now? (y/N): ").lower().strip()
if choice in ['y', 'yes']:
debugger = InteractiveDebugger()
debugger.start_debugging()
else:
print("\n📝 Setup complete! Use VS Code or run interactive_debug.py when ready.")
if __name__ == "__main__":
main()