| 186 | } |
| 187 | |
| 188 | void WallClock::timerLoop() { |
| 189 | int self = OS::threadId(); |
| 190 | ThreadFilter* thread_filter = Profiler::instance()->threadFilter(); |
| 191 | bool thread_filter_enabled = thread_filter->enabled(); |
| 192 | Mode mode = _mode; |
| 193 | |
| 194 | ThreadSleepMap thread_sleep_state; |
| 195 | ThreadList* thread_list = OS::listThreads(); |
| 196 | _thread_cpu_time_buf.reset(); |
| 197 | u64 cycle_start_time = OS::nanotime(); |
| 198 | |
| 199 | while (_running) { |
| 200 | bool enabled = _enabled; |
| 201 | |
| 202 | for (int signaled_threads = 0; signaled_threads < THREADS_PER_TICK && thread_list->hasNext(); ) { |
| 203 | int thread_id = thread_list->next(); |
| 204 | if (thread_id == self || thread_id <= 0) { |
| 205 | // On macOS, task_threads() may sporadically return 0 or -1 among thread IDs |
| 206 | continue; |
| 207 | } |
| 208 | if (thread_filter_enabled && !thread_filter->accept(thread_id)) { |
| 209 | continue; |
| 210 | } |
| 211 | |
| 212 | if (mode == CPU_ONLY) { |
| 213 | if (!enabled || OS::threadState(thread_id) == THREAD_SLEEPING) { |
| 214 | continue; |
| 215 | } |
| 216 | } else if (mode == WALL_BATCH) { |
| 217 | ThreadSleepState& tss = thread_sleep_state[thread_id]; |
| 218 | u64 new_thread_cpu_time = enabled ? OS::threadCpuTime(thread_id) : 0; |
| 219 | if (new_thread_cpu_time != 0 && new_thread_cpu_time - tss.last_cpu_time <= RUNNABLE_THRESHOLD_NS) { |
| 220 | tss.last_time = TSC::ticks(); |
| 221 | if (++tss.counter < MAX_IDLE_BATCH) { |
| 222 | if (tss.counter == 1) { |
| 223 | tss.start_time = tss.last_time; |
| 224 | } |
| 225 | continue; |
| 226 | } |
| 227 | } |
| 228 | if (tss.counter != 0) { |
| 229 | recordWallClock(tss, THREAD_SLEEPING, thread_id); |
| 230 | tss.counter = 0; |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | if (enabled && OS::sendSignalToThread(thread_id, _signal)) { |
| 235 | signaled_threads++; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | u64 current_time = OS::nanotime(); |
| 240 | if (thread_list->hasNext()) { |
| 241 | // Try to keep interval stable regardless of the number of profiled threads |
| 242 | long long sleep_time = cycle_start_time + (u64)_interval * thread_list->index() / thread_list->count() - current_time; |
| 243 | OS::uninterruptibleSleep(sleep_time < MIN_INTERVAL ? MIN_INTERVAL : sleep_time, &_running); |
| 244 | } else { |
| 245 | // Cycle has ended: prepare for the next cycle |