14 KiB
MiniProfiler Project Structure
Directory Layout
MiniProfiler/
├── docs/ # Documentation
│ ├── GETTING_STARTED.md # Quick start guide
│ ├── PROTOCOL.md # Communication protocol specification
│ └── PROJECT_STRUCTURE.md # This file
│
├── host/ # Host application (Python)
│ ├── miniprofiler/ # Main package
│ │ ├── __init__.py # Package initialization
│ │ ├── analyzer.py # Data analysis and visualization data generation
│ │ ├── cli.py # Command-line interface
│ │ ├── protocol.py # Binary protocol implementation
│ │ ├── serial_reader.py # Serial communication
│ │ ├── symbolizer.py # ELF/DWARF symbol resolution
│ │ └── web_server.py # Flask web server with SocketIO
│ │
│ ├── web/ # Web interface assets
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── style.css # Stylesheet
│ │ │ └── js/
│ │ │ └── app.js # JavaScript application logic
│ │ └── templates/
│ │ └── index.html # Main HTML template
│ │
│ ├── tests/ # Tests and utilities
│ │ ├── __init__.py
│ │ └── sample_data_generator.py # Generate mock profiling data
│ │
│ ├── requirements.txt # Python dependencies
│ └── run.py # Quick start script
│
├── embedded/ # Embedded module (Phase 2 - TODO)
│ ├── src/
│ ├── inc/
│ └── examples/
│
├── .gitignore # Git ignore rules
├── CLAUDE.md # Project overview for Claude
└── README.md # Main project README
Module Descriptions
Host Application (host/miniprofiler/)
protocol.py
Purpose: Binary protocol implementation for serial communication
Key Components:
ProfileRecord: Data class for profiling records (14 bytes)Metadata: Device metadata (MCU clock, timer freq, etc.)StatusInfo: Device status informationCommandPacket: Commands sent to deviceResponsePacket: Responses from device- CRC16 calculation and validation
Used by: serial_reader.py, analyzer.py, sample_data_generator.py
serial_reader.py
Purpose: Serial port communication and packet parsing
Key Components:
SerialReader: Main class for serial I/O- Background thread for continuous reading
- State machine for packet parsing
- Callback-based event handling
- Command sending (START, STOP, GET_STATUS, etc.)
Callbacks:
on_profile_data: Profiling records receivedon_metadata: Device metadata receivedon_status: Status update receivedon_error: Error occurred
Used by: web_server.py
symbolizer.py
Purpose: Resolve function addresses to names using ELF/DWARF debug info
Key Components:
Symbolizer: ELF file parser- Loads symbol table from
.elffile - Parses DWARF debug info for file/line mappings
- Address-to-name resolution
- Handles function address ranges
- Loads symbol table from
Dependencies: pyelftools
Used by: analyzer.py, web_server.py
analyzer.py
Purpose: Analyze profiling data and generate visualization data structures
Key Components:
ProfileAnalyzer: Main analysis engine- Build call tree from flat records
- Compute statistics (call counts, durations)
- Generate flame graph data (d3-flame-graph format)
- Generate timeline data (Plotly format)
- Generate statistics table data
Data Structures:
CallTreeNode: Hierarchical call treeFunctionStats: Per-function statistics
Used by: web_server.py
web_server.py
Purpose: Flask web server with SocketIO for real-time updates
Key Components:
ProfilerWebServer: Main server class- Flask HTTP routes (
/,/api/status,/api/flamegraph, etc.) - SocketIO event handlers (connect, start_profiling, etc.)
- Integrates
SerialReader,Symbolizer, andProfileAnalyzer - Real-time data streaming to web clients
- Flask HTTP routes (
Routes:
GET /: Main web interfaceGET /api/status: Server status JSONGET /api/flamegraph: Flame graph data JSONGET /api/timeline: Timeline data JSONGET /api/statistics: Statistics table JSON
SocketIO Events:
connect_serial: Connect to devicestart_profiling: Start profilingstop_profiling: Stop profilingclear_data: Clear all data- Emits:
flamegraph_update,statistics_update, etc.
Used by: cli.py
cli.py
Purpose: Command-line interface entry point
Key Components:
- Argument parsing (--host, --port, --debug, --verbose)
- Logging configuration
- Server initialization and startup
Entry point: miniprofiler command
Web Interface (host/web/)
templates/index.html
Purpose: Main HTML page structure
Features:
- Connection controls (serial port, baud rate, ELF path)
- Profiling controls (start, stop, clear, reset)
- Status display
- Metadata panel
- Summary panel
- Three-tab interface (Flame Graph, Timeline, Statistics)
Dependencies:
- Socket.IO client
- D3.js
- d3-flame-graph
- Plotly.js
static/css/style.css
Purpose: Styling and layout
Features:
- Dark theme (VSCode-inspired)
- Responsive design
- Flexbox layouts
- Custom button styles
- Table styling
- Status indicators with animations
static/js/app.js
Purpose: Client-side application logic
Key Functions:
initializeSocket(): Set up SocketIO connectiontoggleConnection(): Connect/disconnect from devicestartProfiling(),stopProfiling(): Control profilingupdateFlameGraph(): Render flame graph with d3-flame-graphupdateTimeline(): Render timeline with Plotly.jsupdateStatistics(): Update statistics tableshowTab(): Tab switching
Event Handlers:
- Socket events (connect, disconnect, data updates)
- Button clicks
- Window resize
Tests (host/tests/)
sample_data_generator.py
Purpose: Generate realistic mock profiling data for testing
Features:
- Simulates typical embedded application (main, init, loop, sensors, etc.)
- Generates nested function calls with realistic timing
- Creates binary protocol packets
- Exports JSON files for visualization testing
Outputs:
sample_profile_data.bin: Binary protocol datasample_flamegraph.json: Flame graph datasample_statistics.json: Statistics datasample_timeline.json: Timeline data
Usage:
cd host/tests
python sample_data_generator.py
Data Flow
Connection and Initialization
User Web UI Web Server Serial Reader Device
│ │ │ │ │
│─── Open Browser ──►│ │ │ │
│ │ │ │ │
│─── Enter Port ────►│ │ │ │
│─── Click Connect ─►│─── connect_serial ──►│─── connect() ─────►│ │
│ │ │ │─── Open ─────►│
│ │ │ │ │
│ │ │─── get_metadata() ►│─── CMD ──────►│
│ │ │ │◄── METADATA ──│
│ │◄── metadata ─────────│◄── on_metadata() ──│ │
│◄── Display Info ───│ │ │ │
Profiling Session
User Web UI Web Server Analyzer Device
│ │ │ │ │
│─── Start ─────────►│─── start_profiling ─►│─── start() ─────►│ │
│ │ │ │─── CMD ────────►│
│ │ │ │ │
│ │ │ │◄── DATA ────────│
│ │ │◄── on_profile ───│ │
│ │ │ │ │
│ │ │── add_records() ►│ │
│ │ │ │─ Analyze │
│ │ │ │─ Build Tree │
│ │ │ │─ Compute Stats │
│ │ │◄── JSON ─────────│ │
│ │◄─ flamegraph_update ─│ │ │
│◄── Update Viz ─────│ │ │ │
Technology Stack
Backend
- Python 3.8+: Main language
- Flask 3.0+: Web framework
- Flask-SocketIO 5.3+: Real-time WebSocket communication
- pyserial 3.5+: Serial port communication
- pyelftools 0.29+: ELF/DWARF parsing
- crc 6.1+: CRC16 calculation
- eventlet: Async I/O for SocketIO
Frontend
- HTML5/CSS3: Structure and styling
- JavaScript (ES6): Application logic
- Socket.IO Client: Real-time communication
- D3.js v7: Visualization library
- d3-flame-graph 4.1: Flame graph component
- Plotly.js 2.27: Timeline/chart visualization
Development Tools
- setuptools: Package management
- pip: Dependency management
- git: Version control
Configuration Files
requirements.txt
Python package dependencies with minimum versions
.gitignore
Excludes:
- Python bytecode and caches
- Virtual environments
- IDE configs
- Build artifacts
- Generated test data
Key Design Decisions
Why Command-Response Protocol?
- Allows host to control profiling (start/stop)
- Can request status and metadata
- More flexible than auto-start mode
- Small overhead acceptable at 115200 baud
Why Entry Time + Duration?
- Enables both flame graphs (aggregate) and timelines (chronological)
- Only 40% more data than duration-only
- Essential for debugging timing-sensitive embedded systems
Why d3-flame-graph?
- Industry standard for flame graph visualization
- Interactive (zoom, search, tooltips)
- Customizable colors and layout
- Handles large datasets efficiently
Why Separate Analyzer Module?
- Decouples data processing from I/O
- Easier to test in isolation
- Can swap visualization formats without changing protocol
- Allows offline analysis of captured data
Extension Points
Adding New Commands
- Add to
Commandenum inprotocol.py - Implement in
SerialReader.send_command() - Add handler in
web_server.pySocketIO events - Update embedded firmware to handle command
Adding New Visualizations
- Add route in
web_server.py(e.g.,/api/callgraph) - Implement data generation in
analyzer.py - Add HTML tab in
index.html - Add JavaScript rendering in
app.js - Update CSS as needed
Supporting More Microcontrollers
- Ensure GCC toolchain supports
-finstrument-functions - Implement timing mechanism (DWT, SysTick, or custom timer)
- Port ring buffer and UART code to new MCU
- Test and document
Adding Compression
- Update protocol version to 0x02
- Implement compression in embedded module (e.g., delta encoding)
- Add decompression in
protocol.py - Update
ProfileDataPayloadparsing
Future Enhancements
Phase 2: Embedded Module
- STM32 HAL/LL implementation
- FreeRTOS integration
- Example projects for STM32F4/F7/H7
- CMake build system
Phase 3: Advanced Features
- Statistical sampling mode
- ISR profiling
- Multi-core support (dual-core STM32H7)
- Task/thread tracking for RTOS
- Filtering and search
Phase 4: Renode Integration
- Renode platform description
- Virtual UART setup
- CI/CD integration
- Automated regression tests
Phase 5: Analysis Tools
- Differential profiling (compare two runs)
- Export to Chrome Trace Format
- Call graph visualization
- Performance regression detection
- Integration with debuggers (GDB)
Performance Targets
Embedded Overhead
- Target: <5% CPU overhead
- Memory: 2-10 KB RAM for buffers
- Instrumentation: 1-2 μs per function call
Host Performance
- Latency: <100ms from device to visualization
- Throughput: Handle 500-1000 records/sec
- Memory: Scale to 100K+ records in browser
Bandwidth
- 115200 baud: ~780 records/sec
- 460800 baud: ~3100 records/sec
- 921600 baud: ~6200 records/sec
Contributing
See individual module docstrings for implementation details. Follow existing code style and structure when adding features.