]> git.saurik.com Git - apple/system_cmds.git/blob - KDBG/Machine.mutable-impl.hpp
314ecd0055645b0d56b58fe816f5fa69b45ce73b
[apple/system_cmds.git] / KDBG / Machine.mutable-impl.hpp
1 //
2 // Machine.mutable-impl.hpp
3 // KDBG
4 //
5 // Created by James McIlree on 10/30/12.
6 // Copyright (c) 2014 Apple. All rights reserved.
7 //
8
9 #include "KDebug.h"
10
11 template <typename SIZE>
12 MachineProcess<SIZE>* Machine<SIZE>::create_process(pid_t pid, const char* name, AbsTime create_timestamp, kMachineProcessFlag flags) {
13 ASSERT(name, "Sanity");
14
15 // Validate that we are not creating a process that already exists
16 DEBUG_ONLY({
17 ASSERT(_processes_by_pid.size() == _processes_by_name.size(), "Sanity");
18 ASSERT(_processes_by_pid.size() == _processes_by_time.size(), "Sanity");
19
20 auto by_pid_range = _processes_by_pid.equal_range(pid);
21 for (auto it = by_pid_range.first; it != by_pid_range.second; ++it) {
22 MachineProcess<SIZE>& process = it->second;
23 ASSERT(!process.timespan().contains(create_timestamp), "Creating a process that overlaps an existing process");
24 }
25
26 auto by_name_range = _processes_by_name.equal_range(name);
27 for (auto it = by_name_range.first; it != by_name_range.second; ++it) {
28 MachineProcess<SIZE>& process = *it->second;
29 ASSERT((process.pid() != pid) || (!process.timespan().contains(create_timestamp)), "Creating a process that overlaps an existing process");
30 }
31
32 // The "by time" vector is unsorted during construction, we have to look at everything.
33 for (MachineProcess<SIZE>* process : _processes_by_time) {
34 if (process->pid() == pid) {
35 ASSERT(!process->timespan().contains(create_timestamp), "Creating a process that overlaps an existing process");
36 }
37 }
38 })
39
40 if (MachineProcess<SIZE>* about_to_be_reused_process = youngest_mutable_process(pid)) {
41 // If this process is still alive, we're going to be replacing it.
42 // The only legal way of doing that is an exec. Validate.
43 if (!about_to_be_reused_process->is_trace_terminated()) {
44 DEBUG_ONLY({
45 ASSERT((uint32_t)flags & ((uint32_t)kMachineProcessFlag::CreatedByForkExecEvent | (uint32_t)kMachineProcessFlag::CreatedByExecEvent),
46 "Replacing existing process without exec or fork-exec");
47 })
48 //
49 // Exit by exec is unique in that we will have two processes/threads
50 // back to back in the timeline. We do not want them to overlap, and
51 // the new process timeline is half open , and will have this time as
52 // its creation. Pass a 1 mabs older time to exit to prevent overlap
53 about_to_be_reused_process->set_exit_by_exec(create_timestamp - AbsTime(1));
54 }
55 ASSERT(about_to_be_reused_process->is_trace_terminated(), "Sanity");
56 }
57
58 MachineProcess<SIZE>* process = &_processes_by_pid.emplace(pid, MachineProcess<SIZE>(pid, name, create_timestamp, flags))->second;
59 _processes_by_name.emplace(process->name(), process);
60 _processes_by_time.push_back(process);
61
62 return process;
63 }
64
65 template <typename SIZE>
66 MachineThread<SIZE>* Machine<SIZE>::create_thread(MachineProcess<SIZE>* process, typename SIZE::ptr_t tid, MachineVoucher<SIZE>* voucher, AbsTime create_timestamp, kMachineThreadFlag flags) {
67 ASSERT(process, "Sanity");
68
69 // Validate that we are not creating a thread that already exists
70 DEBUG_ONLY({
71 ASSERT(_threads_by_tid.size() == _threads_by_time.size(), "Sanity");
72
73 auto by_tid_range = _threads_by_tid.equal_range(tid);
74 for (auto it = by_tid_range.first; it != by_tid_range.second; ++it) {
75 MachineThread<SIZE>& thread = it->second;
76 ASSERT(!thread.timespan().contains(create_timestamp), "Creating a thread that overlaps an existing thread");
77 }
78
79 // The "by time" vector is unsorted during construction, we have to look at everything
80 for (MachineThread<SIZE>* thread : _threads_by_time) {
81 if (thread->tid() == tid) {
82 ASSERT(!thread->timespan().contains(create_timestamp), "Creating a thread that overlaps an existing thread");
83 }
84 }
85 })
86
87 // Currently the only way we intentionally re-use live threads is via exec/fork-exec.
88 // The exec/fork-exec code calls create_process first, which should mark all existing
89 // threads as trace-terminated. So we should NEVER see a live thread at this point.
90 // validate.
91 DEBUG_ONLY({
92 if (MachineThread<SIZE>* about_to_be_reused_thread = youngest_mutable_thread(tid)) {
93 ASSERT(about_to_be_reused_thread->is_trace_terminated(), "Expected this thread to be terminated");
94 }
95 });
96
97 MachineThread<SIZE>* thread = &_threads_by_tid.emplace(tid, MachineThread<SIZE>(process, tid, voucher, create_timestamp, flags))->second;
98 _threads_by_time.push_back(thread);
99
100 return thread;
101 }
102
103 template <typename SIZE>
104 MachineVoucher<SIZE>* Machine<SIZE>::create_voucher(typename SIZE::ptr_t address, AbsTime create_timestamp, kMachineVoucherFlag flags, uint32_t content_bytes_capacity) {
105 ASSERT(address, "Should not be NULL");
106 ASSERT(content_bytes_capacity < 4096, "Probably an error"); // This is a guesstimate, may need re-evaluation
107
108 MachineVoucher<SIZE>* voucher;
109
110 ASSERT(_voucher_nursery.find(address) == _voucher_nursery.end(), "Attempt to create an already live voucher (<rdar://problem/16898190>)");
111 //
112 // There is no real workaround for this. Other tracepoints will use the address, bad things happen. You can't fix ordering bugs with cleverness outside the lock :-).
113 //
114 // <rdar://problem/16898190> voucher create / destroy tracepoints are outside the hashtable lock
115
116 auto workaround_it = _voucher_nursery.find(address);
117 if (workaround_it != _voucher_nursery.end()) {
118 // We've hit a race condition, this voucher was used before the create event was posted.
119 // We want to update the content_bytes_capacity, but not the create_timestamp.
120 voucher = workaround_it->second.get();
121 voucher->workaround_16898190(flags, content_bytes_capacity);
122 } else {
123 auto it = _voucher_nursery.emplace(address, std::make_unique<MachineVoucher<SIZE>>(address, AbsInterval(create_timestamp, AbsTime(0)), flags, content_bytes_capacity));
124 ASSERT(it.second, "Voucher emplace in nursery failed");
125 voucher = it.first->second.get();
126 }
127
128 ASSERT(voucher->is_live(), "Sanity");
129 ASSERT(!voucher->is_null(), "Sanity");
130 ASSERT(!voucher->is_unset(), "Sanity");
131
132 return voucher;
133 }
134
135 template <typename SIZE>
136 void Machine<SIZE>::destroy_voucher(typename SIZE::ptr_t address, AbsTime timestamp) {
137 ASSERT(address, "Should not be NULL");
138
139 auto nursery_it = _voucher_nursery.find(address);
140
141 // We need a voucher for every reference, so we are in the odd position
142 // of creating a voucher so we can destroy it.
143 if (nursery_it == _voucher_nursery.end()) {
144 create_voucher(address, AbsTime(0), kMachineVoucherFlag::CreatedByFirstUse, 0);
145 nursery_it = _voucher_nursery.find(address);
146 }
147
148 MachineVoucher<SIZE>* voucher = nursery_it->second.get();
149
150 voucher->set_destroyed(timestamp);
151
152 // First find the "row" for this address.
153 auto by_addr_it = _vouchers_by_addr.find(address);
154 if (by_addr_it == _vouchers_by_addr.end()) {
155 // No address entry case
156 //std::vector<std::unique_ptr<MachineVoucher<SIZE>>> row(std::move(nursery_it->second));
157 std::vector<std::unique_ptr<MachineVoucher<SIZE>>> row;
158 row.emplace_back(std::move(nursery_it->second));
159 _vouchers_by_addr.emplace(address, std::move(row));
160 } else {
161 auto& row = by_addr_it->second;
162
163 // Make sure these are sorted and non-overlapping
164 ASSERT(row.back()->timespan() < voucher->timespan(), "Sanity");
165 ASSERT(!row.back()->timespan().intersects(voucher->timespan()), "Sanity");
166
167 row.emplace_back(std::move(nursery_it->second));
168 }
169
170 _voucher_nursery.erase(nursery_it);
171 }
172
173 //
174 // This function handles looking up a voucher by address. If neccessary, it will create a new voucher.
175 // NOTE! Does not update voucher timestamps, that is only done at voucher destroy.
176 //
177
178 template <typename SIZE>
179 MachineVoucher<SIZE>* Machine<SIZE>::process_event_voucher_lookup(typename SIZE::ptr_t address, uint32_t msgh_bits) {
180 // NOTE! There is a subtle race here, we *MUST* test msgh_bits before
181 // checking for a 0 address. An unset voucher may have address 0...
182 if (MACH_MSGH_BITS_VOUCHER(msgh_bits) == MACH_MSGH_BITS_ZERO)
183 return &UnsetVoucher;
184
185 if (address == 0)
186 return &NullVoucher;
187
188 auto nursery_it = _voucher_nursery.find(address);
189 if (nursery_it == _voucher_nursery.end()) {
190 // If no voucher exists, create a default (no-contents!) voucher.
191 return create_voucher(address, AbsTime(0), kMachineVoucherFlag::CreatedByFirstUse, 0);
192 }
193
194 return nursery_it->second.get();
195 }
196
197 template <typename SIZE>
198 MachineVoucher<SIZE>* Machine<SIZE>::thread_forwarding_voucher_lookup(const MachineVoucher<SIZE>* previous_machine_state_voucher) {
199 ASSERT(previous_machine_state_voucher, "Sanity");
200
201 if (previous_machine_state_voucher == &UnsetVoucher)
202 return &UnsetVoucher;
203
204 if (previous_machine_state_voucher == &NullVoucher)
205 return &NullVoucher;
206
207 auto nursery_it = _voucher_nursery.find(previous_machine_state_voucher->address());
208 if (nursery_it == _voucher_nursery.end()) {
209 ASSERT(false, "Should not ever have a thread forwarding a voucher not in the nursery");
210 return &UnsetVoucher;
211 }
212
213 return nursery_it->second.get();
214 }
215
216 //
217 // This is used by processes that are being fork-exec'd / exec'd. They must be
218 // created with some name, but it isn't their final name. For now, we are
219 // heavily ASSERTING state to only allow processes which are fork-exec'd /
220 // exec'd to set their name.
221 //
222 template <typename SIZE>
223 void Machine<SIZE>::set_process_name(MachineProcess<SIZE>* process, const char* name) {
224 ASSERT(process, "Sanity");
225 ASSERT(process->is_created_by_fork_exec() || process->is_created_by_exec(), "Sanity");
226 ASSERT(process->threads().size() == 1, "Sanity");
227 ASSERT(process->is_fork_exec_in_progress() || process->is_exec_in_progress(), "Sanity");
228 ASSERT(name, "Sanity");
229
230 auto by_name_range = _processes_by_name.equal_range(process->name());
231 for (auto it = by_name_range.first; it != by_name_range.second; ++it) {
232 if (process == it->second) {
233 _processes_by_name.erase(it);
234 process->set_name(name);
235 _processes_by_name.emplace(process->name(), process);
236 return;
237 }
238 }
239
240 ASSERT(false, "Attempt to rename did not find a matching process");
241 }
242
243 //
244 // The "youngest" process/thread lookups are used during event processing,
245 // where we often must look up a process/thread that hasn't been updated
246 // to reflect current timespans. A time based lookup would fail.
247 //
248 template <typename SIZE>
249 MachineProcess<SIZE>* Machine<SIZE>::youngest_mutable_process(pid_t pid) {
250 MachineProcess<SIZE>* youngest_process = nullptr;
251 auto by_pid_range = _processes_by_pid.equal_range(pid);
252 for (auto it = by_pid_range.first; it != by_pid_range.second; ++it) {
253 MachineProcess<SIZE>& process = it->second;
254 // Larger times are newer (younger)
255 if (!youngest_process || process.timespan().location() > youngest_process->timespan().location()) {
256 youngest_process = &process;
257 }
258 }
259
260 return youngest_process;
261 }
262
263 template <typename SIZE>
264 MachineThread<SIZE>* Machine<SIZE>::youngest_mutable_thread(typename SIZE::ptr_t tid) {
265 MachineThread<SIZE>* youngest_thread = nullptr;
266 auto by_tid_range = _threads_by_tid.equal_range(tid);
267 for (auto it = by_tid_range.first; it != by_tid_range.second; ++it) {
268 MachineThread<SIZE>& thread = it->second;
269 // Larger times are newer (younger)
270 if (!youngest_thread || thread.timespan().location() > youngest_thread->timespan().location()) {
271 youngest_thread = &thread;
272 }
273 }
274
275 return youngest_thread;
276 }
277
278 //
279 // This function handles looking up a thread by tid. If neccessary, it will create a new thread
280 // and process. Any thread / process that are looked up or created will have their timestamps updated.
281 //
282 template <typename SIZE>
283 MachineThread<SIZE>* Machine<SIZE>::process_event_tid_lookup(typename SIZE::ptr_t tid, AbsTime now) {
284 MachineThread<SIZE>* thread = youngest_mutable_thread(tid);
285
286 if (!thread) {
287 // This is an "unknown" thread. We have no information about its name or parent process.
288 char unknown_process_name[20];
289 snprintf(unknown_process_name, sizeof(unknown_process_name), "unknown-%llX", (uint64_t)tid);
290
291 //
292 // Strongly considering just requiring this to be valid, and never allowing an unknown thread.
293 //
294 printf("UNKNOWN TID FAIL! unknonwn tid %llx\n", (int64_t)tid);
295 ASSERT(false, "unknown TID fail");
296
297 MachineProcess<SIZE>* unknown_process = create_process(next_unknown_pid(), unknown_process_name, now, kMachineProcessFlag::IsUnknownProcess);
298 thread = create_thread(unknown_process, tid, &UnsetVoucher, now, kMachineThreadFlag::CreatedByUnknownTidInTrace);
299 unknown_process->add_thread(thread);
300 }
301
302 ASSERT(thread, "Sanity");
303 ASSERT(!thread->is_trace_terminated(), "Event tid seen after trace termination");
304 ASSERT(!thread->process().is_trace_terminated(), "Event pid seen after trace termination");
305
306 return thread;
307 }
308
309 //
310 // See comments in task_policy.c for full explanation of trequested_0 & trequested_1.
311 //
312 // process_trequested_task means that the tracepoint either had a NULL thread, or specified that the tracepoint was targeted at task level.
313 // This only matters in 32 bit traces, where it takes both trequested_0 and trequested_1 to carry task or thread requested data.
314 //
315 // For now, there is nothing we want to see in thread_requested data.
316 //
317 template <typename SIZE>
318 void Machine<SIZE>::process_trequested_task(pid_t pid, typename SIZE::ptr_t trequested_0, typename SIZE::ptr_t trequested_1) {
319 TaskRequestedPolicy task_requested = (SIZE::is_64_bit) ? TaskRequestedPolicy(trequested_0) : TaskRequestedPolicy((Kernel32::ptr_t)trequested_0, (Kernel32::ptr_t)trequested_1);
320
321 if (uint32_t apptype = (uint32_t)task_requested.as_struct().t_apptype) {
322 if (pid) {
323 if (MachineProcess<SIZE>* target = youngest_mutable_process(pid)) {
324 target->set_apptype_from_trequested(apptype);
325 }
326 }
327 }
328 }
329
330 template <typename SIZE>
331 void Machine<SIZE>::process_trequested_thread(typename SIZE::ptr_t tid, typename SIZE::ptr_t trequested_0, typename SIZE::ptr_t trequested_1) {
332 TaskRequestedPolicy task_requested = (SIZE::is_64_bit) ? TaskRequestedPolicy(trequested_0) : TaskRequestedPolicy((Kernel32::ptr_t)trequested_0, (Kernel32::ptr_t)trequested_1);
333
334 if (uint32_t apptype = (uint32_t)task_requested.as_struct().t_apptype) {
335 if (MachineThread<SIZE>* target_thread = youngest_mutable_thread(tid)) {
336 target_thread->mutable_process().set_apptype_from_trequested(apptype);
337 }
338 }
339 }
340
341 #define AST_PREEMPT 0x01
342 #define AST_QUANTUM 0x02
343 #define AST_URGENT 0x04
344 #define AST_HANDOFF 0x08
345 #define AST_YIELD 0x10
346
347 #define TRACE_DATA_NEWTHREAD 0x07000004
348 #define TRACE_STRING_NEWTHREAD 0x07010004
349 #define TRACE_DATA_EXEC 0x07000008
350 #define TRACE_STRING_EXEC 0x07010008
351 #define TRACE_LOST_EVENTS 0x07020008
352
353 // From ./osfmk/i386/mp.c
354 #define TRACE_MP_CPU_DEACTIVATE MACHDBG_CODE(DBG_MACH_MP, 7)
355
356 // From osfmk/kern/task.h
357 #define TASK_POLICY_TASK 0x4
358 #define TASK_POLICY_THREAD 0x8
359
360 template <typename SIZE>
361 bool Machine<SIZE>::process_event(const KDEvent<SIZE>& event)
362 {
363 ASSERT(!lost_events(), "Should not be processing events after TRACE_LOST_EVENTS");
364
365 AbsTime now = event.timestamp();
366 ASSERT(event.cpu() > -1 && event.cpu() < _cpus.size(), "cpu_id out of range");
367 MachineCPU<SIZE>& cpu = _cpus[event.cpu()];
368
369 if (!cpu.is_iop()) {
370 //
371 // If we have lost events, immediately bail.
372 //
373 // Pre-process events known to have bogus TID's:
374 //
375 // DBG_TRACE_INFO events may not have a valid TID.
376 // MACH_IPC_VOUCHER_CREATE_ATTR_DATA do not have a valid TID,
377 //
378
379 switch (event.dbg_cooked()) {
380 case TRACEDBG_CODE(DBG_TRACE_INFO, 1): // kernel_debug_early_end()
381 case TRACEDBG_CODE(DBG_TRACE_INFO, 4): // kernel_debug_string()
382 return true;
383
384 case TRACEDBG_CODE(DBG_TRACE_INFO, 2): // TRACE_LOST_EVENTS
385 set_flags(kMachineFlag::LostEvents);
386 return false;
387
388 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE_ATTR_DATA):
389 // trace event data is
390 // data, data, data, data
391 //
392 // event tid is voucher address!
393 if (auto voucher = process_event_voucher_lookup(event.tid(), UINT32_MAX)) {
394 voucher->add_content_bytes(event.arg1_as_pointer());
395 }
396 return true;
397
398 default:
399 break;
400 }
401
402 MachineThread<SIZE>* event_thread = process_event_tid_lookup(event.tid(), now);
403
404 switch (event.dbg_cooked()) {
405 case BSDDBG_CODE(DBG_BSD_PROC, BSD_PROC_EXIT): // the exit syscall never returns, use this instead
406 // arg1 == pid exiting
407 // arg2 == exit code
408 if (event.is_func_end()) {
409 // can BSD_PROC_EXIT can be called from another context?
410 ASSERT((pid_t)event.arg1() == event_thread->process().pid(), "proc_exit pid does not match context pid");
411 event_thread->mutable_process().set_exit_by_syscall(now, (int32_t)event.arg2());
412 }
413 break;
414
415 case TRACE_DATA_NEWTHREAD: {
416 ASSERT(event.is_func_none(), "TRACE_DATA_NEWTHREAD event has start/end bit set. Should be func_none.");
417 //
418 // This is called by the thread creating the new thread.
419 //
420 // The thread id of the new thread is in arg1.
421 // The pid of the process creating the new thread is in arg2.
422 //
423 // NOTE! This event carries enough information to create a thread, which we do.
424 // However, the immediately following TRACE_STRING_NEWTHREAD does not have the
425 // newly_created thread's tid. We cannot assume that we will always be able to
426 // read these events as a pair, they may be split by a particularly unlucky
427 // buffer snapshot.
428 //
429 // We have a "thread nursery" which we use to associate the tid of the new
430 // thread with the creating thread.
431 //
432 // (During fork operations, the "parent" may appear different than the child,
433 // this is why we cannot reuse the parent's name and ignore the STRING event.)
434 //
435 auto new_thread_id = (typename SIZE::ptr_t)event.arg1();
436 auto new_thread_pid = (int32_t)event.arg2();
437
438 MachineProcess<SIZE>* new_process = youngest_mutable_process(new_thread_pid);
439 kMachineThreadFlag new_thread_flags;
440
441 //
442 // Okay, it looks like we cannot pay much attention to the source of thread
443 // creates, the system will create a thread for anyone at any time, in any
444 // place. The new model is to lookup the pid of the new thread, and if it
445 // exists and is live, use that. Otherwise, fork-exec a new process.
446 //
447
448 if (new_process) {
449 new_thread_flags = kMachineThreadFlag::CreatedByTraceDataNewThread;
450 } else {
451 new_thread_flags = (kMachineThreadFlag)((uint32_t)kMachineThreadFlag::CreatedByForkExecEvent |
452 (uint32_t)kMachineThreadFlag::IsMain);
453
454 auto new_process_flags = (kMachineProcessFlag)((uint32_t)kMachineProcessFlag::CreatedByForkExecEvent |
455 (uint32_t)kMachineProcessFlag::IsForkExecInProgress);
456
457 // Create the new process
458 new_process = create_process(new_thread_pid, "###Fork#Exec###", now, new_process_flags);
459 }
460 ASSERT(new_process, "Sanity");
461 ASSERT(!new_process->is_trace_terminated(), "Sanity");
462 ASSERT(new_thread_pid != 0 || new_process == _kernel_task, "Sanity");
463 new_process->add_thread(create_thread(new_process, new_thread_id, &UnsetVoucher, now, new_thread_flags));
464 break;
465 }
466
467 case TRACEDBG_CODE(DBG_TRACE_DATA, TRACE_DATA_THREAD_TERMINATE): {
468 ASSERT(event.is_func_none(), "Sanity");
469 typename SIZE::ptr_t terminated_tid = event.arg1();
470 // If tid == terminated_tid, we need to handle the lookup below differently
471 ASSERT(event.tid() != terminated_tid, "Should not be possible");
472 MachineThread<SIZE>* terminated_thread = process_event_tid_lookup(terminated_tid, now);
473 terminated_thread->set_trace_terminated(now);
474
475 // Was this the last thread for a given process?
476 bool all_threads_trace_terminated = true;
477 MachineProcess<SIZE>& process = terminated_thread->mutable_process();
478 for (auto thread : process.threads()) {
479 if (!thread->is_trace_terminated()) {
480 all_threads_trace_terminated = false;
481 break;
482 }
483 }
484
485 if (all_threads_trace_terminated) {
486 process.set_trace_terminated(now);
487 }
488 break;
489 }
490
491 case TRACE_DATA_EXEC: {
492 ASSERT(event.is_func_none(), "TRACE_DATA_EXEC event has start/end bit set. Should be func_none.");
493
494 ASSERT(!event_thread->is_trace_terminated(), "Thread that is trace terminated is exec'ing");
495 ASSERT(!event_thread->process().is_kernel(), "Kernel process is exec'ing");
496 ASSERT(!event_thread->is_idle_thread(), "IDLE thread is exec'ing");
497
498 // arg1 == pid
499 int32_t exec_pid = (int32_t)event.arg1();
500 ASSERT(exec_pid != -1, "Kernel thread is exec'ing");
501 ASSERT(exec_pid == event_thread->process().pid() || event_thread->process().is_unknown(), "Pids should match. If not, maybe vfork?");
502
503 if (event_thread->process().is_fork_exec_in_progress()) {
504 ASSERT(event_thread->process().threads().size() == 1, "Fork invariant violated");
505 // event_thread->mutable_process().clear_fork_exec_in_progress();
506
507 // Hmmm.. Do we need to propagate an apptype here?
508 } else {
509 //
510 // Creating a new process will automagically clean up the
511 // existing one, setting the last known timestamp, and "PidReused"
512 //
513 auto exec_thread_flags = (kMachineThreadFlag)((uint32_t)kMachineThreadFlag::CreatedByExecEvent |
514 (uint32_t)kMachineThreadFlag::IsMain);
515
516 auto exec_process_flags = (kMachineProcessFlag)((uint32_t)kMachineProcessFlag::CreatedByExecEvent |
517 (uint32_t)kMachineProcessFlag::IsExecInProgress);
518
519 auto exec_process = create_process(exec_pid, "###Exec###", now, exec_process_flags);
520 MachineThread<SIZE>* exec_thread = create_thread(exec_process, event_thread->tid(), &UnsetVoucher, now, exec_thread_flags);
521 exec_process->add_thread(exec_thread);
522
523 int32_t apptype = event_thread->process().apptype();
524 if (apptype != -1) {
525 exec_process->set_apptype(apptype);
526 }
527 }
528 break;
529 }
530
531 case TRACE_STRING_EXEC: {
532 ASSERT(event.is_func_none(), "TRACE_STRING_EXEC event has start/end bit set. Should be func_none.");
533 ASSERT(event_thread->mutable_process().is_exec_in_progress() ||
534 event_thread->mutable_process().is_fork_exec_in_progress(), "Must be exec or fork-exec in progress to be here");
535
536 set_process_name(&event_thread->mutable_process(), event.all_args_as_string().c_str());
537
538 if (event_thread->process().is_exec_in_progress())
539 event_thread->mutable_process().clear_exec_in_progress();
540 else
541 event_thread->mutable_process().clear_fork_exec_in_progress();
542 break;
543 }
544
545 case MACHDBG_CODE(DBG_MACH_EXCP_INTR, 0):
546 if (event.is_func_start()) {
547 cpu.set_intr(now);
548 } else {
549 cpu.clear_intr(now);
550 }
551 break;
552
553 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED):
554 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_STACK_HANDOFF): {
555 // The is deactivate switch to idle thread should have happened before we see an actual
556 // context switch for this cpu.
557 ASSERT(!cpu.is_deactivate_switch_to_idle_thread(), "State machine fail");
558
559 typename SIZE::ptr_t handoff_tid = event.arg2();
560 // If the handoff tid and the event_tid are the same, the lookup will fail an assert due to timestamps going backwards.
561 MachineThread<SIZE>* handoff_thread = (handoff_tid == event.tid()) ? event_thread : process_event_tid_lookup(handoff_tid, now);
562 ASSERT(handoff_thread, "Sanity");
563
564 // If we marked a given thread as unrunnable in idle, or the MKRUNNABLE wasn't emitted, make sure we
565 // mark the thread as runnable now.
566 handoff_thread->make_runnable(now);
567 cpu.context_switch(handoff_thread, event_thread, now);
568
569 if (!event_thread->is_idle_thread()) {
570 if (event_thread->tid() != event.arg2()) {
571 if ((event.arg1() & (AST_PREEMPT | AST_QUANTUM | AST_URGENT | AST_HANDOFF | AST_YIELD)) == 0) {
572 event_thread->make_unrunnable(now);
573 }
574 }
575 }
576 break;
577 }
578
579 //
580 // There is a rare case of:
581 //
582 // event[795176] { timestamp=4b8074fa6bb5, arg1=0, arg2=0, arg3=0, arg4=0, tid=8ab77, end MP_CPU_DEACTIVATE, cpu=1 }
583 // event[795177] { timestamp=4b8074fa70bd, arg1=8ab77, arg2=ffffffffffffffff, arg3=0, arg4=4, tid=2d, --- MACH_SCHED_CHOOSE_PROCESSOR, cpu=1 }
584 //
585 // When a cpu shuts down via MP_CPU_DEACTIVATE, on reactivation, the cpu does a forced switch to its idle thread,
586 // without dropping a MACH_SCHED or MACH_STACK_HANDOFF. We want to catch this and update the cpu correctly, as
587 // well as marking the idle thread.
588 //
589 // This is a desktop only codepath, TRACE_MP_CPU_DEACTIVATE is defined in ./osfmk/i386/mp.c
590 //
591 case TRACE_MP_CPU_DEACTIVATE:
592 ASSERT(event_thread == cpu.thread() || !cpu.is_thread_state_initialized(), "Sanity");
593 if (event.is_func_end()) {
594 cpu.set_deactivate_switch_to_idle_thread();
595 }
596 break;
597
598 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED_CHOOSE_PROCESSOR):
599 //
600 // I have seen a sequence of events like this, where it appears that multiple threads get re-dispatched:
601 //
602 // event[254871] { timestamp=332dd22319b, arg1=0, arg2=0, arg3=0, arg4=0, tid=1b8ab, end MP_CPU_DEACTIVATE, cpu=7 }
603 // event[254876] { timestamp=332dd22387a, arg1=1b7d9, arg2=ffffffffffffffff, arg3=e, arg4=4, tid=1b8ab, --- MACH_SCHED_CHOOSE_PROCESSOR, cpu=7 }
604 // event[254877] { timestamp=332dd223c44, arg1=e, arg2=0, arg3=0, arg4=0, tid=1b8ab, --- MACH_SCHED_REMOTE_AST, cpu=7 }
605 // event[254887] { timestamp=332dd22441c, arg1=1b8ab, arg2=ffffffffffffffff, arg3=4, arg4=4, tid=53, --- MACH_SCHED_CHOOSE_PROCESSOR, cpu=7 }
606 //
607 // We will wait until we see a tid mismatch before clearing the deactivate_switch state
608 //
609 if (cpu.is_deactivate_switch_to_idle_thread()) {
610 if (cpu.thread() == NULL || event_thread->tid() != cpu.thread()->tid()) {
611 // The choose tracepoint has the tid of the thread on cpu when it deactivated.
612 ASSERT(cpu.thread() == NULL || cpu.thread()->tid() == event.arg1(), "Sanity");
613
614 cpu.clear_deactivate_switch_to_idle_thread();
615 event_thread->set_is_idle_thread();
616 event_thread->make_runnable(now);
617 cpu.context_switch(event_thread, cpu.thread(), now);
618 }
619 }
620 break;
621
622 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_MAKE_RUNNABLE):
623 event_thread->make_runnable(now);
624 break;
625
626 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_IDLE):
627 if (event.is_func_start()) {
628 cpu.set_idle(now);
629 } else {
630 cpu.clear_idle(now);
631 }
632 break;
633
634 case MACHDBG_CODE(DBG_MACH_VM, 2 /* MACH_vmfault is hardcoded as 2 */):
635 if (event.is_func_start())
636 event_thread->begin_vm_fault(now);
637 else
638 event_thread->end_vm_fault(now);
639 break;
640
641 case BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM):
642 case BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM_HIWAT):
643 if (event.is_func_end()) {
644 if (pid_t pid = (pid_t)event.arg2()) {
645 //
646 // The time for this kill is already covered by the MEMSTAT_scan.
647 // We still want to mark the victim process as jetsam killed, though.
648 // We need to look up the victim, which is the pid in arg2.
649 //
650 if (MachineProcess<SIZE>* victim = youngest_mutable_process(pid)) {
651 ASSERT(!victim->is_exiting(), "Jetsam killing already dead process");
652 // This isn't technically impossible, but as a practical matter it is more likely
653 // signalling a bug than we were able to wrap the pid counter and reuse this pid
654 ASSERT(!victim->is_kernel(), "Cannot jetsam kernel");
655 victim->set_exit_by_jetsam(now);
656 } else {
657 ASSERT(false, "Unable to find jetsam victim pid");
658 }
659 }
660 }
661 break;
662
663 case BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_SCAN):
664 case BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_UPDATE):
665 case BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FREEZE):
666 if (event.is_func_start())
667 event_thread->begin_jetsam_activity(event.dbg_cooked(), now);
668 else
669 event_thread->end_jetsam_activity(event.dbg_cooked(), now);
670 break;
671
672 //
673 // IMP_TASK_APPTYPE trace args are:
674 //
675 // start:
676 // target_pid, trequested_0, trequested_1, apptype
677 // end:
678 // target_pid, trequested_0, trequested_1, is_importance_receiver
679 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_NONE):
680 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_DAEMON_INTERACTIVE):
681 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_DAEMON_STANDARD):
682 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_DAEMON_ADAPTIVE):
683 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_DAEMON_BACKGROUND):
684 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_APP_DEFAULT):
685 case IMPORTANCE_CODE(IMP_TASK_APPTYPE, TASK_APPTYPE_APP_TAL):
686 //
687 // We want to set the explicit apptype now, and trequested will not have the
688 // apptype data until the end event.
689 //
690 if (event.is_func_start()) {
691 if (pid_t pid = (pid_t)event.arg1()) {
692 if (MachineProcess<SIZE>* target = youngest_mutable_process(pid)) {
693 target->set_apptype((uint32_t)event.arg4());
694 }
695 }
696 }
697 process_trequested_task((pid_t)event.arg1(), event.arg2(), event.arg3());
698 break;
699
700 // Trace data is
701 // self_pid, audit_token_pid_from_task(task), trequested_0(task, NULL), trequested_1(task, NULL)
702 case IMPORTANCE_CODE(IMP_TASK_SUPPRESSION, 0):
703 case IMPORTANCE_CODE(IMP_TASK_SUPPRESSION, 1):
704 case IMPORTANCE_CODE(IMP_BOOST, IMP_BOOSTED):
705 case IMPORTANCE_CODE(IMP_BOOST, IMP_UNBOOSTED):
706 process_trequested_task((pid_t)event.arg2(), event.arg3(), event.arg4());
707 break;
708
709 case MACHDBG_CODE(DBG_MACH_IPC, MACH_THREAD_SET_VOUCHER): {
710 //
711 // This can be invoked against another thread; you must use arg1 as the tid.
712 //
713 // thread-tid, name, voucher, callsite-id
714 //
715 auto set_thread_tid = event.arg1();
716 MachineThread<SIZE>* set_thread = (set_thread_tid == event.tid()) ? event_thread : process_event_tid_lookup(set_thread_tid, now);
717 set_thread->set_voucher(process_event_voucher_lookup(event.arg3(), UINT32_MAX), now);
718 break;
719 }
720
721 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE):
722 // trace event data is
723 // voucher address, voucher table size, system voucher count, voucher content bytes
724 create_voucher(event.arg1(), now, kMachineVoucherFlag::CreatedByVoucherCreate, (uint32_t)event.arg4());
725 break;
726
727 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_DESTROY):
728 destroy_voucher(event.arg1(), now);
729 break;
730
731 // The MachMsg state chart...
732 //
733 // The "key" to the mach msg is the kmsg_addr.
734 //
735 // We can encounter a given kmsg_addr in any of
736 // four possible states:
737 //
738 // UNINITIALIZED
739 // SEND
740 // RECV
741 // FREE
742 //
743 // These are the legal state transitions:
744 // (transition to UNINITIALIZED is not possible)
745 //
746 // UNIN -> SEND ; Accept as FREE -> SEND
747 // UNIN -> RECV ; Accept as SEND -> RECV
748 // UNIN -> FREE ; Accept as FREE -> FREE
749 //
750 // SEND -> SEND ; ERROR!
751 // SEND -> RECV ; User to User IPC, send message to machine
752 // SEND -> FREE ; User to Kernel IPC, recycle.
753 //
754 // RECV -> SEND ; ERROR!
755 // RECV -> RECV ; ERROR!
756 // RECV -> FREE ; End User IPC
757 //
758 // FREE -> SEND ; Begin User IPC
759 // FREE -> RECV ; Kernel to User IPC
760 // FREE -> FREE ; Kernel to Kernel IPC
761
762 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_SEND): {
763 // trace event data is:
764 // kmsg_addr, msgh_bits, msgh_id, voucher_addr,
765 auto kmsg_addr = event.arg1();
766 auto msgh_bits = (uint32_t)event.arg2();
767 auto msgh_id = (uint32_t)event.arg3();
768 auto voucher_addr = event.arg4();
769
770 auto nursery_it = _mach_msg_nursery.find(kmsg_addr);
771 if (nursery_it == _mach_msg_nursery.end()) {
772 nursery_it = _mach_msg_nursery.emplace(kmsg_addr, kmsg_addr).first;
773 }
774
775 auto& nursery_msg = nursery_it->second;
776
777 switch (nursery_msg.state()) {
778 // SEND -> SEND ; ERROR!
779 // RECV -> SEND ; ERROR!
780 case kNurseryMachMsgState::Send:
781 ASSERT(false, "illegal state transition (SEND -> SEND) in nursery mach msg");
782 case kNurseryMachMsgState::Recv:
783 ASSERT(false, "illegal state transition (RECV -> SEND) in nursery mach msg");
784 break;
785
786 // UNIN -> SEND ; Accept as FREE -> SEND
787 // FREE -> SEND ; Begin User IPC
788 case kNurseryMachMsgState::Uninitialized:
789 case kNurseryMachMsgState::Free: {
790 uintptr_t event_index = &event - _events;
791 nursery_msg.send(event_index, event.timestamp(), event.tid(), kmsg_addr, msgh_bits, msgh_id, process_event_voucher_lookup(voucher_addr, msgh_bits));
792 break;
793 }
794 }
795 // We do the state set here so that release builds
796 // sync to current state when errors are encountered
797 nursery_msg.set_state(kNurseryMachMsgState::Send);
798 break;
799 }
800
801 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_RECV):
802 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_MSG_RECV_VOUCHER_REFUSED):
803 {
804 // trace event data is
805 // kmsg_addr, msgh_bits, msgh_id, voucher_addr
806 auto kmsg_addr = event.arg1();
807 auto msgh_bits = (uint32_t)event.arg2();
808 auto voucher_addr = event.arg4();
809
810 auto nursery_it = _mach_msg_nursery.find(kmsg_addr);
811 if (nursery_it == _mach_msg_nursery.end()) {
812 nursery_it = _mach_msg_nursery.emplace(kmsg_addr, kmsg_addr).first;
813 }
814
815 auto& nursery_msg = nursery_it->second;
816
817 uint32_t flags = (event.dbg_code() == MACH_IPC_MSG_RECV_VOUCHER_REFUSED) ? kMachineMachMsgFlag::IsVoucherRefused : 0;
818 uintptr_t event_index = &event - _events;
819
820 switch (nursery_msg.state()) {
821
822 // UNIN -> RECV ; Accept as SEND -> RECV
823 case kNurseryMachMsgState::Uninitialized: {
824 flags |= kMachineMachMsgFlag::HasReceiver;
825
826 auto mach_msg_it = _mach_msgs.emplace(_mach_msgs.end(),
827 NurseryMachMsg<SIZE>::message_id(),
828 kmsg_addr,
829 flags,
830 AbsTime(0),
831 0,
832 0,
833 &Machine<SIZE>::UnsetVoucher,
834 now,
835 event.tid(),
836 msgh_bits,
837 process_event_voucher_lookup(voucher_addr, msgh_bits));
838
839 ASSERT(_mach_msgs_by_event_index.find(event_index) == _mach_msgs_by_event_index.end(), "Stomping mach msg");
840 _mach_msgs_by_event_index[event_index] = std::distance(_mach_msgs.begin(), mach_msg_it);
841 break;
842 }
843
844 // SEND -> RECV ; User to User IPC, send message to machine
845 case kNurseryMachMsgState::Send: {
846 ASSERT(kmsg_addr == nursery_msg.kmsg_addr(), "Sanity");
847 ASSERT((uint32_t)event.arg3() == nursery_msg.send_msgh_id(), "Sanity");
848
849 flags |= (kMachineMachMsgFlag::HasSender | kMachineMachMsgFlag::HasReceiver);
850
851 auto mach_msg_it = _mach_msgs.emplace(_mach_msgs.end(),
852 nursery_msg.id(),
853 kmsg_addr,
854 flags,
855 nursery_msg.send_time(),
856 nursery_msg.send_tid(),
857 nursery_msg.send_msgh_bits(),
858 nursery_msg.send_voucher(),
859 now,
860 event.tid(),
861 msgh_bits,
862 process_event_voucher_lookup(voucher_addr, msgh_bits));
863
864 intptr_t send_event_index = nursery_msg.send_event_index();
865 if (send_event_index != -1) {
866 ASSERT(send_event_index < _event_count, "Sanity");
867 ASSERT(_mach_msgs_by_event_index.find(event_index) == _mach_msgs_by_event_index.end(), "Stomping mach msg");
868 _mach_msgs_by_event_index[send_event_index] = std::distance(_mach_msgs.begin(), mach_msg_it);
869 }
870 ASSERT(_mach_msgs_by_event_index.find(event_index) == _mach_msgs_by_event_index.end(), "Stomping mach msg");
871 _mach_msgs_by_event_index[event_index] = std::distance(_mach_msgs.begin(), mach_msg_it);
872 break;
873 }
874
875 // RECV -> RECV ; ERROR!
876 case kNurseryMachMsgState::Recv:
877 ASSERT(false, "illegal state transition (RECV -> RECV) in nursery mach msg");
878 break;
879
880 // FREE -> RECV ; Kernel to User IPC
881 case kNurseryMachMsgState::Free:
882 break;
883 }
884
885 // We do the state set here so that release builds
886 // sync to current state when errors are encountered
887 nursery_msg.set_state(kNurseryMachMsgState::Recv);
888 break;
889 }
890
891 case MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_KMSG_FREE): {
892 // trace event data is:
893 // kmsg_addr
894
895 auto kmsg_addr = event.arg1();
896
897 auto nursery_it = _mach_msg_nursery.find(kmsg_addr);
898 if (nursery_it == _mach_msg_nursery.end()) {
899 nursery_it = _mach_msg_nursery.emplace(kmsg_addr, kmsg_addr).first;
900 }
901
902 auto& nursery_msg = nursery_it->second;
903
904
905 // All transitions to FREE are legal.
906 nursery_msg.set_state(kNurseryMachMsgState::Free);
907 }
908
909 default:
910 // IO Activity
911 //
912 // There isn't an easy way to handle these inside the switch, The
913 // code is used as a bitfield.
914
915
916 //
917 // Okay temp note on how to approach this.
918 //
919 // Even a single thread may have overlapping IO activity.
920 // None of the current scheme's handle overlapped activity well.
921 //
922 // We'd like to be able to show for any given interval, "X pages IO outstanding, Y pages completed, Z ms waiting"
923 //
924 // To do that, we've got to be able to intersect an arbitrary interval with a pile of overlapping intervals.
925 //
926 // The approach is to accumulate the IO activity into a single vector.
927 // Sort by interval.location().
928 // Now flatten this interval (union flatten).
929 // This will produce a second vector of non-overlapping intervals.
930 // When we want to intersect the arbitrary interval, we do the standard search on the non overlapping interval vector.
931 // This will give us a starting and ending location that guarantee to cover every IO that might intersect.
932 //
933 // The assumption is that while IO's overlap, they don't stay active forever. Sooner or later there will be a break.
934 //
935 // The arch-nemesis of this scheme is the light overlap, like so:
936 //
937 // XXXXX XXXXXXXXXXX XXXXXXXXXXXXXXXXXXXX
938 // XXXXXXX XXXXXXXXXXXXXXXX XXXXXXXXXXXXXX
939
940
941 //
942 // It turns out that IO can overlap inside a single thread, for example:
943 //
944 // 437719 C73AD5945 --- P_RdDataAsync 209b9f07 1000002 6b647 5000 2A72 1 gamed 293
945 // 437724 C73AD5DCA --- P_RdDataAsync 209b7e37 1000002 6b64c 6000 2A72 1 gamed 293
946 // 437822 C73AD98B0 --- P_RdDataAsyncDone 209b7e37 4dfe3eef 0 0 191 1 kernel_task 0
947 // 437829 C73AD9E55 --- P_RdDataAsyncDone 209b9f07 4dfe3eef 0 0 191 1 kernel_task 0
948 //
949
950 if (event.dbg_class() == DBG_FSYSTEM && event.dbg_subclass() == DBG_DKRW) {
951 uint32_t code = event.dbg_code();
952 //
953 // Disk IO doesn't use func_start/func_end
954 //
955 // arg1 == uid
956 // arg4 == size
957 if (code & DKIO_DONE) {
958 this->end_io(now, event.arg1());
959 } else {
960
961 // IO is initiated by a given process/thread, but it always finishes on a kernel_thread.
962 // We need to stash enough data to credit the correct thread when the completion event arrives.
963 begin_io(event_thread, now, event.arg1(), event.arg4());
964 }
965 } else if (event.dbg_class() == DBG_IMPORTANCE) {
966 //
967 // Every task policy set trace code carries "trequested" data, we would like to grab them all.
968 //
969 // This subclass spans the range of 0x20 through 0x3F
970 //
971
972 uint32_t subclass = event.dbg_subclass();
973 if (subclass >= 0x20 && subclass <= 0x3F) {
974 // Trace event data is
975 // targetid(task, thread), trequested_0(task, thread), trequested_1(task, thread), value
976
977 bool is_task_event = (event.dbg_code() & TASK_POLICY_TASK) > 0;
978
979 // Should not be both a task and thread event.
980 ASSERT(is_task_event != (event.dbg_code() & TASK_POLICY_THREAD), "BEWM!");
981
982 if (is_task_event) {
983 process_trequested_task((pid_t)event.arg1(), event.arg2(), event.arg3());
984 } else {
985 process_trequested_thread(event.arg1(), event.arg2(), event.arg3());
986 }
987 }
988 }
989 break;
990 }
991 }
992
993 return true;
994 }
995
996 template <typename SIZE>
997 void Machine<SIZE>::initialize_cpu_idle_intr_states() {
998 ASSERT(_event_count, "Sanity");
999 ASSERT(_events, "Sanity");
1000 ASSERT(!_cpus.empty(), "Sanity");
1001
1002 // How much work do we need to do?
1003 uint32_t inits_needed = 0;
1004 uint32_t inits_done = 0;
1005 for (auto& cpu : _cpus) {
1006 if (!cpu.is_iop()) {
1007 inits_needed += 3;
1008
1009 if (cpu.is_intr_state_initialized()) {
1010 inits_done++;
1011 }
1012 if (cpu.is_idle_state_initialized()) {
1013 inits_done++;
1014 }
1015 if (cpu.is_thread_state_initialized()) {
1016 inits_done++;
1017 }
1018 }
1019 }
1020
1021 uintptr_t index;
1022 for (index = 0; index < _event_count; ++index) {
1023 const KDEvent<SIZE>& event = _events[index];
1024 ASSERT(event.cpu() > -1 && event.cpu() < _cpus.size(), "cpu_id out of range");
1025 MachineCPU<SIZE>& cpu = _cpus[event.cpu()];
1026
1027 if (!cpu.is_iop()) {
1028 switch (event.dbg_cooked()) {
1029 case TRACE_LOST_EVENTS:
1030 // We're done, give up.
1031 return;
1032
1033 case MACHDBG_CODE(DBG_MACH_EXCP_INTR, 0):
1034 if (!cpu.is_intr_state_initialized()) {
1035 inits_done++;
1036
1037 if (event.is_func_start()) {
1038 // If we are starting an INTR now, the cpu was not in INTR prior to now.
1039 cpu.initialize_intr_state(false, _events[0].timestamp());
1040 } else {
1041 // If we are ending an INTR now, the cpu was in INTR prior to now.
1042 cpu.initialize_intr_state(true, _events[0].timestamp());
1043 }
1044 }
1045 break;
1046
1047 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_IDLE):
1048 if (!cpu.is_idle_state_initialized()) {
1049 inits_done++;
1050
1051 if (event.is_func_start()) {
1052 // If we are starting Idle now, the cpu was not Idle prior to now.
1053 cpu.initialize_idle_state(false, _events[0].timestamp());
1054 } else {
1055 // If we are ending Idle now, the cpu was Idle prior to now.
1056 cpu.initialize_idle_state(true, _events[0].timestamp());
1057 }
1058 }
1059 break;
1060
1061 // I spent a day tracking this down....
1062 //
1063 // When you are actively sampling (say, every 100ms) on a machine
1064 // that is mostly idle, there will be long periods of VERY idle
1065 // cpus. So you might get a sample with no begin/end idle at all,
1066 // but the cpu is actually idle the entire time. Now suppose in
1067 // the next sample, you get a simple idle timeout in the middle,
1068 // and immdiately go back to idle. If we treat any TID found on
1069 // cpu as "running", we blow up because this segment appears to
1070 // have the idle thread "running".
1071 //
1072 // So, to do a proper thread init, we require actual scheduler
1073 // activity to tell us who the thread was.
1074 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_SCHED):
1075 case MACHDBG_CODE(DBG_MACH_SCHED, MACH_STACK_HANDOFF):
1076 if (!cpu.is_thread_state_initialized()) {
1077 inits_done++;
1078
1079 // We want to use the thread that *was* on cpu, not the thread being
1080 // handed off too.
1081 MachineThread<SIZE>* init_thread = youngest_mutable_thread(event.tid());
1082 // Legal for this to be NULL!
1083 cpu.initialize_thread_state(init_thread, _events[0].timestamp());
1084
1085 }
1086 break;
1087 }
1088 }
1089
1090 if (inits_done == inits_needed) {
1091 break;
1092 }
1093 }
1094 }
1095
1096 template <typename SIZE>
1097 void Machine<SIZE>::begin_io(MachineThread<SIZE>* thread, AbsTime begin_time, typename SIZE::ptr_t uid, typename SIZE::ptr_t size) {
1098 auto it = _io_by_uid.find(uid);
1099 if (it == _io_by_uid.end()) {
1100 _io_by_uid.emplace(uid, IOActivity<SIZE>(begin_time, AbsTime(0), thread, size));
1101 } else {
1102 // We shouldn't find a valid IO entry at the uid we're installing.
1103 ASSERT(it->second.thread() == NULL, "Overwriting existing io entry");
1104 ASSERT(it->second.location() == 0, "Overwriting existing io entry");
1105
1106 it->second = IOActivity<SIZE>(begin_time, AbsTime(0), thread, size);
1107 }
1108 }
1109
1110 template <typename SIZE>
1111 void Machine<SIZE>::end_io(AbsTime end_time, typename SIZE::ptr_t uid) {
1112 auto it = _io_by_uid.find(uid);
1113
1114 // Its okay to not find a match, if a trace begins with a Done event, for example.
1115 if (it != _io_by_uid.end()) {
1116 MachineThread<SIZE>* io_thread = it->second.thread();
1117 AbsTime begin_time = it->second.location();
1118 ASSERT(end_time > it->second.location(), "Sanity");
1119
1120 _all_io.emplace_back(begin_time, end_time - begin_time, io_thread, it->second.size());
1121
1122 DEBUG_ONLY({
1123 it->second.set_thread(NULL);
1124 it->second.set_location(AbsTime(0));
1125 it->second.set_length(AbsTime(0));
1126 })
1127 }
1128 }