| 33 | |
| 34 | |
| 35 | class ProfileRunner: |
| 36 | def __init__( |
| 37 | self, |
| 38 | target: str, |
| 39 | iterations: int = 20, |
| 40 | build_mode: str = "release", |
| 41 | use_docker: bool = False, |
| 42 | skip_generate: bool = False, |
| 43 | force_regenerate: bool = False, |
| 44 | use_callgrind: bool = False, |
| 45 | debuggable: bool = False, |
| 46 | ): |
| 47 | self.target = target |
| 48 | self.iterations = iterations |
| 49 | # Auto-select build mode based on flags |
| 50 | # Priority: callgrind > debuggable > user-specified |
| 51 | if use_callgrind and build_mode == "release": |
| 52 | # Callgrind needs DWARF4 debug symbols (Valgrind 3.19 compatibility) |
| 53 | self.build_mode = "profile" |
| 54 | print( |
| 55 | "ℹ️ Callgrind mode: Using 'profile' build mode (DWARF4 for Valgrind 3.19)" |
| 56 | ) |
| 57 | elif debuggable and build_mode == "release": |
| 58 | # Debuggable mode needs stack traces |
| 59 | self.build_mode = "quick" |
| 60 | print( |
| 61 | "ℹ️ Debuggable mode: Using 'quick' build mode for stack trace preservation" |
| 62 | ) |
| 63 | else: |
| 64 | self.build_mode = build_mode |
| 65 | self.use_docker = use_docker |
| 66 | self.skip_generate = skip_generate |
| 67 | self.force_regenerate = force_regenerate |
| 68 | self.use_callgrind = use_callgrind |
| 69 | self.debuggable = debuggable |
| 70 | self.test_name = self._sanitize_name(target) |
| 71 | self.test_path = Path(f"tests/profile/{self.test_name}.cpp") |
| 72 | self.results_file = Path(f"{self.test_name}_results.json") |
| 73 | |
| 74 | def _sanitize_name(self, name: str) -> str: |
| 75 | """Convert 'fl::sincos16' → 'sincos16'""" |
| 76 | return ( |
| 77 | name.replace("::", "_") |
| 78 | .replace("<", "_") |
| 79 | .replace(">", "_") |
| 80 | .replace(" ", "_") |
| 81 | .lower() |
| 82 | ) |
| 83 | |
| 84 | def generate_profiler(self) -> bool: |
| 85 | """Generate profiler test with smart defaults (no interactive prompts)""" |
| 86 | # Case 1: Profiler exists and we're not forcing regeneration |
| 87 | if self.test_path.exists() and not self.force_regenerate: |
| 88 | if self.skip_generate: |
| 89 | print(f"Using existing profiler: {self.test_path}") |
| 90 | else: |
| 91 | print(f"Using existing profiler: {self.test_path}") |
| 92 | print(" (Use --regenerate to recreate from template)") |