| 84 | ) |
| 85 | |
| 86 | def sample(self, collector, duration_sec=None, *, async_aware=False): |
| 87 | sample_interval_sec = self.sample_interval_usec / 1_000_000 |
| 88 | num_samples = 0 |
| 89 | errors = 0 |
| 90 | interrupted = False |
| 91 | running_time_sec = 0 |
| 92 | start_time = next_time = time.perf_counter() |
| 93 | last_sample_time = start_time |
| 94 | realtime_update_interval = 1.0 # Update every second |
| 95 | last_realtime_update = start_time |
| 96 | try: |
| 97 | while duration_sec is None or running_time_sec < duration_sec: |
| 98 | # Check if live collector wants to stop |
| 99 | if hasattr(collector, 'running') and not collector.running: |
| 100 | break |
| 101 | |
| 102 | current_time = time.perf_counter() |
| 103 | if next_time > current_time: |
| 104 | sleep_time = (next_time - current_time) * 0.9 |
| 105 | if sleep_time > 0.0001: |
| 106 | time.sleep(sleep_time) |
| 107 | elif next_time < current_time: |
| 108 | try: |
| 109 | with _pause_threads(self.unwinder, self.blocking): |
| 110 | if async_aware == "all": |
| 111 | stack_frames = self.unwinder.get_all_awaited_by() |
| 112 | elif async_aware == "running": |
| 113 | stack_frames = self.unwinder.get_async_stack_trace() |
| 114 | else: |
| 115 | stack_frames = self.unwinder.get_stack_trace() |
| 116 | collector.collect(stack_frames) |
| 117 | except ProcessLookupError as e: |
| 118 | running_time_sec = current_time - start_time |
| 119 | break |
| 120 | except (RuntimeError, UnicodeDecodeError, MemoryError, OSError): |
| 121 | collector.collect_failed_sample() |
| 122 | errors += 1 |
| 123 | except Exception as e: |
| 124 | if not _is_process_running(self.pid): |
| 125 | break |
| 126 | raise e from None |
| 127 | |
| 128 | # Track actual sampling intervals for real-time stats |
| 129 | if num_samples > 0: |
| 130 | actual_interval = current_time - last_sample_time |
| 131 | self.sample_intervals.append( |
| 132 | 1.0 / actual_interval |
| 133 | ) # Convert to Hz |
| 134 | self.total_samples += 1 |
| 135 | |
| 136 | # Print real-time statistics if enabled |
| 137 | if ( |
| 138 | self.realtime_stats |
| 139 | and (current_time - last_realtime_update) |
| 140 | >= realtime_update_interval |
| 141 | ): |
| 142 | self._print_realtime_stats() |
| 143 | last_realtime_update = current_time |