Collect samples from stack frames. Args: stack_frames: List of interpreter/thread frame info timestamps_us: List of timestamps in microseconds (None for live sampling)
(self, stack_frames, timestamps_us=None)
| 143 | current_time, category) |
| 144 | |
| 145 | def collect(self, stack_frames, timestamps_us=None): |
| 146 | """Collect samples from stack frames. |
| 147 | |
| 148 | Args: |
| 149 | stack_frames: List of interpreter/thread frame info |
| 150 | timestamps_us: List of timestamps in microseconds (None for live sampling) |
| 151 | """ |
| 152 | # Handle live sampling (no timestamps provided) |
| 153 | if timestamps_us is None: |
| 154 | current_time = (time.monotonic() * 1000) - self.start_time |
| 155 | times = [current_time] |
| 156 | else: |
| 157 | if not timestamps_us: |
| 158 | return |
| 159 | # Initialize base timestamp if needed |
| 160 | if self._replay_base_timestamp_us is None: |
| 161 | self._replay_base_timestamp_us = timestamps_us[0] |
| 162 | # Convert all timestamps to times (ms relative to first sample) |
| 163 | base = self._replay_base_timestamp_us |
| 164 | times = [(ts - base) / 1000 for ts in timestamps_us] |
| 165 | |
| 166 | first_time = times[0] |
| 167 | |
| 168 | # Update interval calculation |
| 169 | if self.sample_count > 0 and self.last_sample_time > 0: |
| 170 | self.interval = (times[-1] - self.last_sample_time) / self.sample_count |
| 171 | self.last_sample_time = times[-1] |
| 172 | |
| 173 | # Process threads |
| 174 | for interpreter_info in stack_frames: |
| 175 | for thread_info in interpreter_info.threads: |
| 176 | frames = filter_internal_frames(thread_info.frame_info) |
| 177 | tid = thread_info.thread_id |
| 178 | status_flags = thread_info.status |
| 179 | is_main_thread = bool(status_flags & THREAD_STATUS_MAIN_THREAD) |
| 180 | |
| 181 | # Initialize thread if needed |
| 182 | if tid not in self.threads: |
| 183 | self.threads[tid] = self._create_thread(tid, is_main_thread) |
| 184 | |
| 185 | thread_data = self.threads[tid] |
| 186 | |
| 187 | # Decode status flags |
| 188 | has_gil = bool(status_flags & THREAD_STATUS_HAS_GIL) |
| 189 | on_cpu = bool(status_flags & THREAD_STATUS_ON_CPU) |
| 190 | gil_requested = bool(status_flags & THREAD_STATUS_GIL_REQUESTED) |
| 191 | |
| 192 | # Track state transitions using first timestamp |
| 193 | self._track_state_transition( |
| 194 | tid, has_gil, self.has_gil_start, self.no_gil_start, |
| 195 | "Has GIL", "No GIL", CATEGORY_GIL, first_time |
| 196 | ) |
| 197 | self._track_state_transition( |
| 198 | tid, on_cpu, self.on_cpu_start, self.off_cpu_start, |
| 199 | "On CPU", "Off CPU", CATEGORY_CPU, first_time |
| 200 | ) |
| 201 | |
| 202 | # Track code type |