Generate a comprehensive report with optional call graph analysis
(
board_name: str,
symbols: list[SymbolInfo],
dependencies: dict[str, list[str]],
call_graph: Optional[dict[str, list[str]]] = None,
reverse_call_graph: Optional[dict[str, list[str]]] = None,
enhanced_mode: bool = False,
)
| 557 | |
| 558 | |
| 559 | def generate_report( |
| 560 | board_name: str, |
| 561 | symbols: list[SymbolInfo], |
| 562 | dependencies: dict[str, list[str]], |
| 563 | call_graph: Optional[dict[str, list[str]]] = None, |
| 564 | reverse_call_graph: Optional[dict[str, list[str]]] = None, |
| 565 | enhanced_mode: bool = False, |
| 566 | ) -> AnalysisReport: |
| 567 | """Generate a comprehensive report with optional call graph analysis""" |
| 568 | print("\n" + "=" * 80) |
| 569 | if enhanced_mode and call_graph and reverse_call_graph: |
| 570 | print(f"{board_name.upper()} ENHANCED SYMBOL ANALYSIS REPORT") |
| 571 | else: |
| 572 | print(f"{board_name.upper()} SYMBOL ANALYSIS REPORT") |
| 573 | print("=" * 80) |
| 574 | |
| 575 | # Summary statistics |
| 576 | total_symbols = len(symbols) |
| 577 | symbols_with_size = [s for s in symbols if s.size > 0] |
| 578 | symbols_without_size = [s for s in symbols if s.size == 0] |
| 579 | |
| 580 | # Calculate total size from ONLY sized symbols |
| 581 | total_size = sum(s.size for s in symbols_with_size) |
| 582 | |
| 583 | print("\nSUMMARY:") |
| 584 | print(f" Total symbols: {total_symbols}") |
| 585 | print(f" Symbols with size info: {len(symbols_with_size)}") |
| 586 | print(f" Symbols without size (elided by linker): {len(symbols_without_size)}") |
| 587 | print(f" Total symbol size: {total_size} bytes ({total_size / 1024:.1f} KB)") |
| 588 | |
| 589 | if enhanced_mode and call_graph and reverse_call_graph: |
| 590 | print(f" Functions with calls: {len(call_graph)}") |
| 591 | print(f" Functions called by others: {len(reverse_call_graph)}") |
| 592 | |
| 593 | # Show source breakdown |
| 594 | source_stats: dict[str, int] = {} |
| 595 | for sym in symbols: |
| 596 | source = sym.source |
| 597 | if source not in source_stats: |
| 598 | source_stats[source] = 0 |
| 599 | source_stats[source] += 1 |
| 600 | |
| 601 | print("\nSYMBOL SOURCES:") |
| 602 | for source, count in sorted(source_stats.items()): |
| 603 | print(f" {source}: {count} symbols") |
| 604 | |
| 605 | # Largest symbols overall (FILTER OUT ZERO-SIZE SYMBOLS) |
| 606 | print("\nLARGEST SYMBOLS (all symbols, sorted by size):") |
| 607 | |
| 608 | # Filter to only symbols with size > 0 (exclude elided/unused symbols) |
| 609 | sized_symbols = [s for s in symbols if s.size > 0] |
| 610 | symbols_sorted = sorted(sized_symbols, key=lambda x: x.size, reverse=True) |
| 611 | |
| 612 | zero_size_count = len(symbols) - len(sized_symbols) |
| 613 | if zero_size_count > 0: |
| 614 | print( |
| 615 | f" (Filtered out {zero_size_count} zero-size symbols that were elided by linker)" |
| 616 | ) |