277 lines
10 KiB
Python
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() |