+ if (is_pending) :
+ pending_time = call.tc_pending_timestamp - recent_timestamp
+ pending_time = kern.GetNanotimeFromAbstime(pending_time) / 1000000000.0
+
+ flags = int(call.tc_flags)
+ # TODO: extract this out of the thread_call_flags_t enum
+ thread_call_flags = {0x0:'', 0x1:'A', 0x2:'W', 0x4:'D', 0x8:'R', 0x10:'S', 0x20:'O',
+ 0x40:'P', 0x80:'L', 0x100:'C'}
+
+ flags_str = ''
+ mask = 0x1
+ while mask <= 0x100 :
+ flags_str += thread_call_flags[int(flags & mask)]
+ mask = mask << 1
+
+ if is_iotes :
+ flags_str += 'I'
+
+ if (is_pending) :
+ print ("{:s}{:#018x}: {:18d} {:18d} {:16.06f} {:16.06f} {:16.06f} {:9s} " +
+ "{:#018x} ({:#018x}, {:#018x}) ({:s}) {:s}").format(prefix,
+ unsigned(call), call.tc_pqlink.deadline, call.tc_soft_deadline, ttd_s,
+ timer_fire_s, pending_time, flags_str,
+ func, param0, param1, func_name, extra_string)
+ else :
+ print ("{:s}{:#018x}: {:18d} {:18d} {:16.06f} {:16.06f} {:9s} " +
+ "{:#018x} ({:#018x}, {:#018x}) ({:s}) {:s}").format(prefix,
+ unsigned(call), call.tc_pqlink.deadline, call.tc_soft_deadline, ttd_s,
+ timer_fire_s, flags_str,
+ func, param0, param1, func_name, extra_string)
+
+ShowThreadCall.enable_debug = False
+
+@header("{:>18s} {:>18s} {:>18s} {:>16s} {:>16s} {:9s} {:>18s}".format(
+ "entry", "deadline", "soft_deadline",
+ "duration (s)", "to go (s)", "flags", "(*func) (param0, param1)"))
+def PrintThreadGroup(group):
+ header = PrintThreadGroup.header
+ pending_header = "{:>18s} {:>18s} {:>18s} {:>16s} {:>16s} {:>16s} {:9s} {:>18s}".format(
+ "entry", "deadline", "soft_deadline",
+ "duration (s)", "to go (s)", "pending", "flags", "(*func) (param0, param1)")
+
+ recent_timestamp = GetRecentTimestamp()
+
+ idle_timestamp_distance = group.idle_timestamp - recent_timestamp
+ idle_timestamp_distance_s = kern.GetNanotimeFromAbstime(idle_timestamp_distance) / 1000000000.0
+
+ is_parallel = ""
+
+ if (group.tcg_flags & GetEnumValue('thread_call_group_flags_t::TCG_PARALLEL')) :
+ is_parallel = " (parallel)"
+
+ print "Group: {g.tcg_name:s} ({:#18x}){:s}".format(unsigned(group), is_parallel, g=group)
+ print "\t" +"Thread Priority: {g.tcg_thread_pri:d}\n".format(g=group)
+ print ("\t" +"Active: {g.active_count:<3d} Idle: {g.idle_count:<3d}" +
+ "Blocked: {g.blocked_count:<3d} Pending: {g.pending_count:<3d}" +
+ "Target: {g.target_thread_count:<3d}\n").format(g=group)
+
+ if unsigned(group.idle_timestamp) is not 0 :
+ print "\t" +"Idle Timestamp: {g.idle_timestamp:d} ({:03.06f})\n".format(idle_timestamp_distance_s,
+ g=group)
+
+ print "\t" +"Pending Queue: ({:>#18x})\n".format(addressof(group.pending_queue))
+ if not LinkageChainEmpty(group.pending_queue) :
+ print "\t\t" + pending_header
+ for call in ParanoidIterateLinkageChain(group.pending_queue, "thread_call_t", "tc_qlink"):
+ ShowThreadCall("\t\t", call, recent_timestamp, is_pending=True)
+
+ print "\t" +"Delayed Queue (Absolute Time): ({:>#18x}) timer: ({:>#18x})\n".format(
+ addressof(group.delayed_queues[0]), addressof(group.delayed_timers[0]))
+ if not LinkageChainEmpty(group.delayed_queues[0]) :
+ print "\t\t" + header
+ for call in ParanoidIterateLinkageChain(group.delayed_queues[0], "thread_call_t", "tc_qlink"):
+ ShowThreadCall("\t\t", call, recent_timestamp)
+
+ print "\t" +"Delayed Queue (Continuous Time): ({:>#18x}) timer: ({:>#18x})\n".format(
+ addressof(group.delayed_queues[1]), addressof(group.delayed_timers[1]))
+ if not LinkageChainEmpty(group.delayed_queues[1]) :
+ print "\t\t" + header
+ for call in ParanoidIterateLinkageChain(group.delayed_queues[1], "thread_call_t", "tc_qlink"):
+ ShowThreadCall("\t\t", call, recent_timestamp)
+
+def PrintThreadCallThreads() :
+ callout_flag = GetEnumValue('thread_tag_t::THREAD_TAG_CALLOUT')
+ recent_timestamp = GetRecentTimestamp()
+
+ for thread in IterateQueue(kern.globals.kernel_task.threads, 'thread *', 'task_threads'):
+ if (thread.thread_tag & callout_flag) :
+ print " {:#20x} {:#12x} {:s}".format(thread, thread.thread_id, GetThreadName(thread))
+ state = thread.thc_state
+ if state and state.thc_call :
+ print "\t" + PrintThreadGroup.header
+ ShowThreadCall("\t", state.thc_call, recent_timestamp)
+ soft_deadline = state.thc_call_soft_deadline
+ slop_time = state.thc_call_hard_deadline - soft_deadline
+ slop_time = kern.GetNanotimeFromAbstime(slop_time) / 1000000000.0
+ print "\t original soft deadline {:d}, hard deadline {:d} (leeway {:.06f}s)".format(
+ soft_deadline, state.thc_call_hard_deadline, slop_time)
+ enqueue_time = state.thc_call_pending_timestamp - soft_deadline
+ enqueue_time = kern.GetNanotimeFromAbstime(enqueue_time) / 1000000000.0
+ print "\t time to enqueue after deadline: {:.06f}s (enqueued at: {:d})".format(
+ enqueue_time, state.thc_call_pending_timestamp)
+ wait_time = state.thc_call_start - state.thc_call_pending_timestamp
+ wait_time = kern.GetNanotimeFromAbstime(wait_time) / 1000000000.0
+ print "\t time to start executing after enqueue: {:.06f}s (executing at: {:d})".format(
+ wait_time, state.thc_call_start)
+
+ if (state.thc_IOTES_invocation_timestamp) :
+ iotes_acquire_time = state.thc_IOTES_invocation_timestamp - state.thc_call_start
+ iotes_acquire_time = kern.GetNanotimeFromAbstime(iotes_acquire_time) / 1000000000.0
+ print "\t IOTES acquire time: {:.06f}s (acquired at: {:d})".format(
+ iotes_acquire_time, state.thc_IOTES_invocation_timestamp)
+
+
+@lldb_command('showcalloutgroup')
+def ShowCalloutGroup(cmd_args=None):
+ """ Prints out the pending and delayed thread calls for a specific group
+
+ Pass 'threads' to show the thread call threads themselves.
+
+ Callout flags:
+
+ A - Allocated memory owned by thread_call.c
+ W - Wait - thread waiting for call to finish running
+ D - Delayed - deadline based
+ R - Running - currently executing on a thread
+ S - Signal - call from timer interrupt instead of thread
+ O - Once - pend the enqueue if re-armed while running
+ P - Reschedule pending - enqueue is pending due to re-arm while running
+ L - Rate-limited - (App Nap)
+ C - Continuous time - Timeout is in mach_continuous_time
+ I - Callout is an IOTimerEventSource
+ """
+ if not cmd_args:
+ print "No arguments passed"
+ print ShowCalloutGroup.__doc__
+ return False
+
+ if "threads" in cmd_args[0] :
+ PrintThreadCallThreads()
+ return
+
+ group = kern.GetValueFromAddress(cmd_args[0], 'struct thread_call_group *')
+ if not group:
+ print "unknown arguments:", str(cmd_args)
+ return False
+
+ PrintThreadGroup(group)