| 63 | |
| 64 | |
| 65 | class GeckoCollector(Collector): |
| 66 | def __init__(self, sample_interval_usec, *, skip_idle=False, opcodes=False): |
| 67 | self.sample_interval_usec = sample_interval_usec |
| 68 | self.skip_idle = skip_idle |
| 69 | self.opcodes_enabled = opcodes |
| 70 | self.start_time = time.monotonic() * 1000 # milliseconds since start |
| 71 | |
| 72 | # Global string table (shared across all threads) |
| 73 | self.global_strings = ["(root)"] # Start with root |
| 74 | self.global_string_map = {"(root)": 0} |
| 75 | |
| 76 | # Per-thread data structures |
| 77 | self.threads = {} # tid -> thread data |
| 78 | |
| 79 | # Global tables |
| 80 | self.libs = [] |
| 81 | |
| 82 | # Sampling interval tracking |
| 83 | self.sample_count = 0 |
| 84 | self.last_sample_time = 0 |
| 85 | self.interval = 1.0 # Will be calculated from actual sampling |
| 86 | |
| 87 | # State tracking for interval markers (tid -> start_time) |
| 88 | self.has_gil_start = {} # Thread has the GIL |
| 89 | self.no_gil_start = {} # Thread doesn't have the GIL |
| 90 | self.on_cpu_start = {} # Thread is running on CPU |
| 91 | self.off_cpu_start = {} # Thread is off CPU |
| 92 | self.python_code_start = {} # Thread running Python code (has GIL) |
| 93 | self.native_code_start = {} # Thread running native code (on CPU without GIL) |
| 94 | self.gil_wait_start = {} # Thread waiting for GIL |
| 95 | self.exception_start = {} # Thread has an exception set |
| 96 | self.no_exception_start = {} # Thread has no exception set |
| 97 | |
| 98 | # GC event tracking: track GC start time per thread |
| 99 | self.gc_start_per_thread = {} # tid -> start_time |
| 100 | |
| 101 | # Track which threads have been initialized for state tracking |
| 102 | self.initialized_threads = set() |
| 103 | |
| 104 | # Opcode state tracking per thread: tid -> (opcode, lineno, col_offset, funcname, filename, start_time) |
| 105 | self.opcode_state = {} |
| 106 | |
| 107 | # For binary replay: track base timestamp (first sample's timestamp) |
| 108 | self._replay_base_timestamp_us = None |
| 109 | |
| 110 | def _track_state_transition(self, tid, condition, active_dict, inactive_dict, |
| 111 | active_name, inactive_name, category, current_time): |
| 112 | """Track binary state transitions and emit markers. |
| 113 | |
| 114 | Args: |
| 115 | tid: Thread ID |
| 116 | condition: Whether the active state is true |
| 117 | active_dict: Dict tracking start time of active state |
| 118 | inactive_dict: Dict tracking start time of inactive state |
| 119 | active_name: Name for active state marker |
| 120 | inactive_name: Name for inactive state marker |
| 121 | category: Gecko category for the markers |
| 122 | current_time: Current timestamp |
no outgoing calls
searching dependent graphs…