]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | """ Please make sure you read the README file COMPLETELY BEFORE reading anything below. | |
3 | It is very critical that you read coding guidelines in Section E in README file. | |
4 | """ | |
5 | ||
6 | from xnu import * | |
7 | import sys, shlex | |
8 | from utils import * | |
9 | from core.lazytarget import * | |
10 | import time | |
11 | import xnudefines | |
12 | import memory | |
13 | import json | |
14 | from collections import defaultdict | |
15 | ||
16 | def GetProcName(proc): | |
17 | """ returns a string name of the process. Longer variant is preffered if provided. | |
18 | params: | |
19 | proc: value object representing a proc in the kernel. | |
20 | returns: | |
21 | str: a string name of the process linked to the task. | |
22 | """ | |
23 | name = str(proc.p_name) | |
24 | if name != '': | |
25 | return name | |
26 | else: | |
27 | return str(proc.p_comm) | |
28 | ||
29 | def GetProcNameForTask(task): | |
30 | """ returns a string name of the process. If proc is not valid the proc | |
31 | name is looked up in the associated importance structure (if | |
32 | available). If no name can be found, "unknown" is returned. | |
33 | params: | |
34 | task: value object represeting a task in the kernel. | |
35 | returns: | |
36 | str : A string name of the process linked to the task | |
37 | """ | |
38 | if task: | |
39 | if unsigned(task.bsd_info): | |
40 | p = Cast(task.bsd_info, 'proc *') | |
41 | return GetProcName(p) | |
42 | ||
43 | if (hasattr(task, 'task_imp_base') and | |
44 | hasattr(task.task_imp_base, 'iit_procname') and | |
45 | unsigned(task.task_imp_base) != 0): | |
46 | return str(task.task_imp_base.iit_procname) | |
47 | ||
48 | return "unknown" | |
49 | ||
50 | def GetProcPIDForTask(task): | |
51 | """ returns a int pid of the process. if the proc is not valid, val[5] from audit_token is returned. | |
52 | params: | |
53 | task: value object representing a task in the kernel | |
54 | returns: | |
55 | int : pid of the process or -1 if not found | |
56 | """ | |
57 | if task and unsigned(task.bsd_info): | |
58 | p = Cast(task.bsd_info, 'proc *') | |
59 | return unsigned(p.p_pid) | |
60 | ||
61 | if task : | |
62 | return unsigned(task.audit_token.val[5]) | |
63 | ||
64 | return -1 | |
65 | ||
66 | def GetProcInfo(proc): | |
67 | """ returns a string name, pid, parent and task for a proc_t. Decodes cred, flag and p_stat fields. | |
68 | params: | |
69 | proc : value object representing a proc in the kernel | |
70 | returns: | |
71 | str : A string describing various information for process. | |
72 | """ | |
73 | out_string = "" | |
74 | out_string += ("Process {p: <#020x}\n\tname {0: <32s}\n\tpid:{p.p_pid: <6d} " + | |
75 | "task:{p.task: <#020x} p_stat:{p.p_stat: <6d} parent pid: {p.p_ppid: <6d}\n" | |
76 | ).format(GetProcName(proc), p=proc) | |
77 | #print the Creds | |
78 | ucred = proc.p_ucred | |
79 | if ucred: | |
80 | out_string += "Cred: euid {:d} ruid {:d} svuid {:d}\n".format(ucred.cr_posix.cr_uid, | |
81 | ucred.cr_posix.cr_ruid, | |
82 | ucred.cr_posix.cr_svuid ) | |
83 | #print the flags | |
84 | flags = int(proc.p_flag) | |
85 | out_string += "Flags: {0: <#020x}\n".format(flags) | |
86 | i = 1 | |
87 | num = 1 | |
88 | while num <= flags: | |
89 | if flags & num: | |
90 | out_string += "\t" + xnudefines.proc_flag_explain_strings[i] + "\n" | |
91 | elif num == 0x4: #special case for 32bit flag | |
92 | out_string += "\t" + xnudefines.proc_flag_explain_strings[0] + "\n" | |
93 | i += 1 | |
94 | num = num << 1 | |
95 | out_string += "State: " | |
96 | state_val = proc.p_stat | |
97 | if state_val < 1 or state_val > len(xnudefines.proc_state_strings) : | |
98 | out_string += "(Unknown)" | |
99 | else: | |
100 | out_string += xnudefines.proc_state_strings[int(state_val)] | |
101 | ||
102 | return out_string | |
103 | ||
104 | def GetProcNameForPid(pid): | |
105 | """ Finds the name of the process corresponding to a given pid | |
106 | params: | |
107 | pid : int, pid you want to find the procname for | |
108 | returns | |
109 | str : Name of the process corresponding to the pid, "Unknown" if not found | |
110 | """ | |
111 | for p in kern.procs: | |
112 | if int(p.p_pid) == int(pid): | |
113 | return GetProcName(p) | |
114 | return "Unknown" | |
115 | ||
116 | def GetProcForPid(search_pid): | |
117 | """ Finds the value object representing a proc in the kernel based on its pid | |
118 | params: | |
119 | search_pid : int, pid whose proc structure you want to find | |
120 | returns: | |
121 | value : The value object representing the proc, if a proc corresponding | |
122 | to the given pid is found. Returns None otherwise | |
123 | """ | |
124 | if search_pid == 0: | |
125 | return kern.globals.initproc | |
126 | else: | |
127 | headp = kern.globals.allproc | |
128 | for proc in IterateListEntry(headp, 'struct proc *', 'p_list'): | |
129 | if proc.p_pid == search_pid: | |
130 | return proc | |
131 | return None | |
132 | ||
133 | @lldb_command('allproc') | |
134 | def AllProc(cmd_args=None): | |
135 | """ Walk through the allproc structure and print procinfo for each process structure. | |
136 | params: | |
137 | cmd_args - [] : array of strings passed from lldb command prompt | |
138 | """ | |
139 | for proc in kern.procs : | |
140 | print GetProcInfo(proc) | |
141 | ||
142 | ||
143 | @lldb_command('zombproc') | |
144 | def ZombProc(cmd_args=None): | |
145 | """ Routine to print out all procs in the zombie list | |
146 | params: | |
147 | cmd_args - [] : array of strings passed from lldb command prompt | |
148 | """ | |
149 | if len(kern.zombprocs) != 0: | |
150 | print "\nZombie Processes:" | |
151 | for proc in kern.zombprocs: | |
152 | print GetProcInfo(proc) + "\n\n" | |
153 | ||
154 | @lldb_command('zombtasks') | |
155 | def ZombTasks(cmd_args=None): | |
156 | """ Routine to print out all tasks in the zombie list | |
157 | params: None | |
158 | """ | |
159 | out_str = "" | |
160 | if len(kern.zombprocs) != 0: | |
161 | header = "\nZombie Tasks:\n" | |
162 | header += GetTaskSummary.header + " " + GetProcSummary.header | |
163 | for proc in kern.zombprocs: | |
164 | if proc.p_stat != 5: | |
165 | t = Cast(proc.task, 'task *') | |
166 | out_str += GetTaskSummary(t) +" "+ GetProcSummary(proc) + "\n" | |
167 | if out_str != "": | |
168 | print header | |
169 | print out_str | |
170 | ||
171 | @lldb_command('zombstacks') | |
172 | def ZombStacks(cmd_args=None): | |
173 | """ Routine to print out all stacks of tasks that are exiting | |
174 | """ | |
175 | header_flag = 0 | |
176 | for proc in kern.zombprocs: | |
177 | if proc.p_stat != 5: | |
178 | if header_flag == 0: | |
179 | print "\nZombie Stacks:" | |
180 | header_flag = 1 | |
181 | t = Cast(proc.task, 'task *') | |
182 | ShowTaskStacks(t) | |
183 | #End of Zombstacks | |
184 | ||
185 | def GetASTSummary(ast): | |
186 | """ Summarizes an AST field | |
187 | Flags: | |
188 | P - AST_PREEMPT | |
189 | Q - AST_QUANTUM | |
190 | U - AST_URGENT | |
191 | H - AST_HANDOFF | |
192 | Y - AST_YIELD | |
193 | A - AST_APC | |
194 | L - AST_LEDGER | |
195 | B - AST_BSD | |
196 | K - AST_KPERF | |
197 | M - AST_MACF | |
198 | r - AST_RESET_PCS | |
199 | a - AST_ARCADE | |
200 | G - AST_GUARD | |
201 | T - AST_TELEMETRY_USER | |
202 | T - AST_TELEMETRY_KERNEL | |
203 | T - AST_TELEMETRY_WINDOWED | |
204 | S - AST_SFI | |
205 | D - AST_DTRACE | |
206 | I - AST_TELEMETRY_IO | |
207 | E - AST_KEVENT | |
208 | R - AST_REBALANCE | |
209 | N - AST_UNQUIESCE | |
210 | """ | |
211 | out_string = "" | |
212 | state = int(ast) | |
213 | thread_state_chars = {0x0:'', 0x1:'P', 0x2:'Q', 0x4:'U', 0x8:'H', 0x10:'Y', 0x20:'A', | |
214 | 0x40:'L', 0x80:'B', 0x100:'K', 0x200:'M', 0x400: 'r', 0x800: 'a', | |
215 | 0x1000:'G', 0x2000:'T', 0x4000:'T', 0x8000:'T', 0x10000:'S', | |
216 | 0x20000: 'D', 0x40000: 'I', 0x80000: 'E', 0x100000: 'R', 0x200000: 'N'} | |
217 | state_str = '' | |
218 | mask = 0x1 | |
219 | while mask <= 0x200000: | |
220 | state_str += thread_state_chars[int(state & mask)] | |
221 | mask = mask << 1 | |
222 | ||
223 | return state_str | |
224 | ||
225 | ||
226 | @lldb_type_summary(['kcdata_descriptor *', 'kcdata_descriptor_t']) | |
227 | @header("{0: <20s} {1: <20s} {2: <20s} {3: <10s} {4: <5s}".format("kcdata_descriptor", "begin_addr", "cur_pos", "size", "flags")) | |
228 | def GetKCDataSummary(kcdata): | |
229 | """ Summarizes kcdata_descriptor structure | |
230 | params: kcdata: value - value object representing kcdata_descriptor | |
231 | returns: str - summary of the kcdata object | |
232 | """ | |
233 | format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <10d} {4: <#05x}" | |
234 | return format_string.format(kcdata, kcdata.kcd_addr_begin, kcdata.kcd_addr_end, kcdata.kcd_length, kcdata.kcd_flags) | |
235 | ||
236 | ||
237 | @lldb_type_summary(['task', 'task_t']) | |
238 | @header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: <5s}".format("task","vm_map", "ipc_space", "#acts", "flags")) | |
239 | def GetTaskSummary(task, showcorpse=False): | |
240 | """ Summarizes the important fields in task structure. | |
241 | params: task: value - value object representing a task in kernel | |
242 | returns: str - summary of the task | |
243 | """ | |
244 | out_string = "" | |
245 | format_string = '{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: <5s}' | |
246 | thread_count = int(task.thread_count) | |
247 | task_flags = '' | |
248 | if hasattr(task, "suppression_generation") and (int(task.suppression_generation) & 0x1) == 0x1: | |
249 | task_flags += 'P' | |
250 | if hasattr(task, "effective_policy") and int(task.effective_policy.tep_sup_active) == 1: | |
251 | task_flags += 'N' | |
252 | if hasattr(task, "suspend_count") and int(task.suspend_count) > 0: | |
253 | task_flags += 'S' | |
254 | if hasattr(task, 'task_imp_base') and unsigned(task.task_imp_base): | |
255 | tib = task.task_imp_base | |
256 | if int(tib.iit_receiver) == 1: | |
257 | task_flags += 'R' | |
258 | if int(tib.iit_donor) == 1: | |
259 | task_flags += 'D' | |
260 | if int(tib.iit_assertcnt) > 0: | |
261 | task_flags += 'B' | |
262 | ||
263 | # check if corpse flag is set | |
264 | if unsigned(task.t_flags) & 0x20: | |
265 | task_flags += 'C' | |
266 | if unsigned(task.t_flags) & 0x40: | |
267 | task_flags += 'P' | |
268 | ||
269 | out_string += format_string.format(task, task.map, task.itk_space, thread_count, task_flags) | |
270 | if showcorpse is True and unsigned(task.corpse_info) != 0: | |
271 | out_string += " " + GetKCDataSummary(task.corpse_info) | |
272 | return out_string | |
273 | ||
274 | def GetThreadName(thread): | |
275 | """ Get the name of a thread, if possible. Returns the empty string | |
276 | otherwise. | |
277 | """ | |
278 | if int(thread.uthread) != 0: | |
279 | uthread = Cast(thread.uthread, 'uthread *') | |
280 | if int(uthread.pth_name) != 0 : | |
281 | th_name_strval = Cast(uthread.pth_name, 'char *') | |
282 | if len(str(th_name_strval)) > 0 : | |
283 | return str(th_name_strval) | |
284 | ||
285 | return '' | |
286 | ||
287 | @lldb_type_summary(['thread *', 'thread_t']) | |
288 | @header("{0: <24s} {1: <10s} {2: <20s} {3: <6s} {4: <6s} {5: <15s} {6: <15s} {7: <8s} {8: <12s} {9: <32s} {10: <20s} {11: <20s} {12: <20s}".format('thread', 'thread_id', 'processor', 'base', 'pri', 'sched_mode', 'io_policy', 'state', 'ast', 'waitq', 'wait_event', 'wmesg', 'thread_name')) | |
289 | def GetThreadSummary(thread): | |
290 | """ Summarize the thread structure. It decodes the wait state and waitevents from the data in the struct. | |
291 | params: thread: value - value objecte representing a thread in kernel | |
292 | returns: str - summary of a thread | |
293 | ||
294 | State flags: | |
295 | W - WAIT | |
296 | S - SUSP | |
297 | R - RUN | |
298 | U - Uninterruptible | |
299 | H - Terminated | |
300 | A - Terminated and on termination queue | |
301 | I - Idle thread | |
302 | C - Crashed thread | |
303 | ||
304 | policy flags: | |
305 | B - darwinbg | |
306 | T - IO throttle | |
307 | P - IO passive | |
308 | D - Terminated | |
309 | """ | |
310 | out_string = "" | |
311 | format_string = "{0: <24s} {1: <10s} {2: <20s} {3: <6s} {4: <6s} {5: <15s} {6: <15s} {7: <8s} {8: <12s} {9: <32s} {10: <20s} {11: <20s} {12: <20s}" | |
312 | thread_ptr_str = str("{0: <#020x}".format(thread)) | |
313 | if int(thread.static_param) : | |
314 | thread_ptr_str+="[WQ]" | |
315 | thread_id = hex(thread.thread_id) | |
316 | processor = hex(thread.last_processor) | |
317 | base_priority = str(int(thread.base_pri)) | |
318 | sched_priority = str(int(thread.sched_pri)) | |
319 | sched_mode = '' | |
320 | mode = str(thread.sched_mode) | |
321 | if "TIMESHARE" in mode: | |
322 | sched_mode+="timeshare" | |
323 | elif "FIXED" in mode: | |
324 | sched_mode+="fixed" | |
325 | elif "REALTIME" in mode: | |
326 | sched_mode+="realtime" | |
327 | ||
328 | if (unsigned(thread.bound_processor) != 0): | |
329 | sched_mode+=" bound" | |
330 | ||
331 | # TH_SFLAG_THROTTLED | |
332 | if (unsigned(thread.sched_flags) & 0x0004): | |
333 | sched_mode+=" BG" | |
334 | ||
335 | io_policy_str = "" | |
336 | thread_name = GetThreadName(thread) | |
337 | if int(thread.uthread) != 0: | |
338 | uthread = Cast(thread.uthread, 'uthread *') | |
339 | ||
340 | #check for io_policy flags | |
341 | if int(uthread.uu_flag) & 0x400: | |
342 | io_policy_str+='RAGE ' | |
343 | ||
344 | #now flags for task_policy | |
345 | ||
346 | io_policy_str = "" | |
347 | ||
348 | if int(thread.effective_policy.thep_darwinbg) != 0: | |
349 | io_policy_str += "B" | |
350 | if int(thread.effective_policy.thep_io_tier) != 0: | |
351 | io_policy_str += "T" | |
352 | if int(thread.effective_policy.thep_io_passive) != 0: | |
353 | io_policy_str += "P" | |
354 | if int(thread.effective_policy.thep_terminated) != 0: | |
355 | io_policy_str += "D" | |
356 | ||
357 | state = int(thread.state) | |
358 | thread_state_chars = {0x0:'', 0x1:'W', 0x2:'S', 0x4:'R', 0x8:'U', 0x10:'H', 0x20:'A', 0x40:'P', 0x80:'I'} | |
359 | state_str = '' | |
360 | mask = 0x1 | |
361 | while mask <= 0x80 : | |
362 | state_str += thread_state_chars[int(state & mask)] | |
363 | mask = mask << 1 | |
364 | ||
365 | if int(thread.inspection): | |
366 | state_str += 'C' | |
367 | ||
368 | ast = int(thread.ast) | int(thread.reason) | |
369 | ast_str = GetASTSummary(ast) | |
370 | ||
371 | #wait queue information | |
372 | wait_queue_str = '' | |
373 | wait_event_str = '' | |
374 | wait_message = '' | |
375 | if ( state & 0x1 ) != 0: | |
376 | #we need to look at the waitqueue as well | |
377 | wait_queue_str = str("{0: <#020x}".format(int(hex(thread.waitq), 16))) | |
378 | wait_event_str = str("{0: <#020x}".format(int(hex(thread.wait_event), 16))) | |
379 | wait_event_str_sym = kern.Symbolicate(int(hex(thread.wait_event), 16)) | |
380 | if len(wait_event_str_sym) > 0: | |
381 | wait_event_str = wait_event_str.strip() + " <" + wait_event_str_sym + ">" | |
382 | if int(thread.uthread) != 0 : | |
383 | uthread = Cast(thread.uthread, 'uthread *') | |
384 | if int(uthread.uu_wmesg) != 0: | |
385 | wait_message = str(Cast(uthread.uu_wmesg, 'char *')) | |
386 | ||
387 | out_string += format_string.format(thread_ptr_str, thread_id, processor, base_priority, sched_priority, sched_mode, io_policy_str, state_str, ast_str, wait_queue_str, wait_event_str, wait_message, thread_name) | |
388 | return out_string | |
389 | ||
390 | ||
391 | def GetTaskRoleString(role): | |
392 | role_strs = { | |
393 | 0 : "TASK_UNSPECIFIED", | |
394 | 1 : "TASK_FOREGROUND_APPLICATION", | |
395 | 2 : "TASK_BACKGROUND_APPLICATION", | |
396 | 3 : "TASK_CONTROL_APPLICATION", | |
397 | 4 : "TASK_GRAPHICS_SERVER", | |
398 | 5 : "TASK_THROTTLE_APPLICATION", | |
399 | 6 : "TASK_NONUI_APPLICATION", | |
400 | 7 : "TASK_DEFAULT_APPLICATION", | |
401 | } | |
402 | return role_strs[int(role)] | |
403 | ||
404 | def GetCoalitionFlagString(coal): | |
405 | flags = [] | |
406 | if (coal.privileged): | |
407 | flags.append('privileged') | |
408 | if (coal.termrequested): | |
409 | flags.append('termrequested') | |
410 | if (coal.terminated): | |
411 | flags.append('terminated') | |
412 | if (coal.reaped): | |
413 | flags.append('reaped') | |
414 | if (coal.notified): | |
415 | flags.append('notified') | |
416 | if (coal.efficient): | |
417 | flags.append('efficient') | |
418 | return "|".join(flags) | |
419 | ||
420 | def GetCoalitionTasks(queue, coal_type, thread_details=False): | |
421 | sfi_strs = { | |
422 | 0x0 : "SFI_CLASS_UNSPECIFIED", | |
423 | 0x1 : "SFI_CLASS_DARWIN_BG", | |
424 | 0x2 : "SFI_CLASS_APP_NAP", | |
425 | 0x3 : "SFI_CLASS_MANAGED_FOCAL", | |
426 | 0x4 : "SFI_CLASS_MANAGED_NONFOCAL", | |
427 | 0x5 : "SFI_CLASS_DEFAULT_FOCAL", | |
428 | 0x6 : "SFI_CLASS_DEFAULT_NONFOCAL", | |
429 | 0x7 : "SFI_CLASS_KERNEL", | |
430 | 0x8 : "SFI_CLASS_OPTED_OUT", | |
431 | 0x9 : "SFI_CLASS_UTILITY", | |
432 | 0xA : "SFI_CLASS_LEGACY_FOCAL", | |
433 | 0xB : "SFI_CLASS_LEGACY_NONFOCAL", | |
434 | 0xC : "SFI_CLASS_USER_INITIATED_FOCAL", | |
435 | 0xD : "SFI_CLASS_USER_INITIATED_NONFOCAL", | |
436 | 0xE : "SFI_CLASS_USER_INTERACTIVE_FOCAL", | |
437 | 0xF : "SFI_CLASS_USER_INTERACTIVE_NONFOCAL", | |
438 | 0x10 : "SFI_CLASS_MAINTENANCE", | |
439 | } | |
440 | tasks = [] | |
441 | field_name = 'task_coalition' | |
442 | for task in IterateLinkageChain(queue, 'task *', field_name, coal_type * sizeof('queue_chain_t')): | |
443 | task_str = "({0: <d},{1: #x}, {2: <s}, {3: <s})".format(GetProcPIDForTask(task),task,GetProcNameForTask(task),GetTaskRoleString(task.effective_policy.tep_role)) | |
444 | if thread_details: | |
445 | for thread in IterateQueue(task.threads, "thread_t", "task_threads"): | |
446 | task_str += "\n\t\t\t|-> thread:" + hex(thread) + ", " + sfi_strs[int(thread.sfi_class)] | |
447 | tasks.append(task_str) | |
448 | return tasks | |
449 | ||
450 | def GetCoalitionTypeString(type): | |
451 | """ Convert a coalition type field into a string | |
452 | Currently supported types (from <mach/coalition.h>): | |
453 | COALITION_TYPE_RESOURCE | |
454 | COALITION_TYPE_JETSAM | |
455 | """ | |
456 | if type == 0: # COALITION_TYPE_RESOURCE | |
457 | return 'RESOURCE' | |
458 | if type == 1: | |
459 | return 'JETSAM' | |
460 | return '<unknown>' | |
461 | ||
462 | def GetResourceCoalitionSummary(coal, verbose=False): | |
463 | """ Summarize a resource coalition | |
464 | """ | |
465 | out_string = "Resource Coalition:\n\t Ledger:\n" | |
466 | thread_details = False | |
467 | if config['verbosity'] > vSCRIPT: | |
468 | thread_details = True | |
469 | ledgerp = coal.r.ledger | |
470 | if verbose and unsigned(ledgerp) != 0: | |
471 | i = 0 | |
472 | while i != ledgerp.l_template.lt_cnt: | |
473 | out_string += "\t\t" | |
474 | out_string += GetLedgerEntrySummary(kern.globals.task_ledger_template, ledgerp.l_entries[i], i) | |
475 | i = i + 1 | |
476 | out_string += "\t bytesread {0: <d}\n\t byteswritten {1: <d}\n\t gpu_time {2: <d}".format(coal.r.bytesread, coal.r.byteswritten, coal.r.gpu_time) | |
477 | out_string += "\n\t total_tasks {0: <d}\n\t dead_tasks {1: <d}\n\t active_tasks {2: <d}".format(coal.r.task_count, coal.r.dead_task_count, coal.r.task_count - coal.r.dead_task_count) | |
478 | out_string += "\n\t last_became_nonempty_time {0: <d}\n\t time_nonempty {1: <d}".format(coal.r.last_became_nonempty_time, coal.r.time_nonempty) | |
479 | out_string += "\n\t cpu_ptime {0: <d}".format(coal.r.cpu_ptime) | |
480 | if verbose: | |
481 | out_string += "\n\t cpu_time_effective[THREAD_QOS_DEFAULT] {0: <d}".format(coal.r.cpu_time_eqos[0]) | |
482 | out_string += "\n\t cpu_time_effective[THREAD_QOS_MAINTENANCE] {0: <d}".format(coal.r.cpu_time_eqos[1]) | |
483 | out_string += "\n\t cpu_time_effective[THREAD_QOS_BACKGROUND] {0: <d}".format(coal.r.cpu_time_eqos[2]) | |
484 | out_string += "\n\t cpu_time_effective[THREAD_QOS_UTILITY] {0: <d}".format(coal.r.cpu_time_eqos[3]) | |
485 | out_string += "\n\t cpu_time_effective[THREAD_QOS_LEGACY] {0: <d}".format(coal.r.cpu_time_eqos[4]) | |
486 | out_string += "\n\t cpu_time_effective[THREAD_QOS_USER_INITIATED] {0: <d}".format(coal.r.cpu_time_eqos[5]) | |
487 | out_string += "\n\t cpu_time_effective[THREAD_QOS_USER_INTERACTIVE] {0: <d}".format(coal.r.cpu_time_eqos[6]) | |
488 | out_string += "\n\t Tasks:\n\t\t" | |
489 | tasks = GetCoalitionTasks(addressof(coal.r.tasks), 0, thread_details) | |
490 | out_string += "\n\t\t".join(tasks) | |
491 | return out_string | |
492 | ||
493 | def GetJetsamCoalitionSummary(coal, verbose=False): | |
494 | out_string = "Jetsam Coalition:" | |
495 | thread_details = False | |
496 | if config['verbosity'] > vSCRIPT: | |
497 | thread_details = True | |
498 | if unsigned(coal.j.leader) == 0: | |
499 | out_string += "\n\t NO Leader!" | |
500 | else: | |
501 | out_string += "\n\t Leader:\n\t\t" | |
502 | out_string += "({0: <d},{1: #x}, {2: <s}, {3: <s})".format(GetProcPIDForTask(coal.j.leader),coal.j.leader,GetProcNameForTask(coal.j.leader),GetTaskRoleString(coal.j.leader.effective_policy.tep_role)) | |
503 | out_string += "\n\t Extensions:\n\t\t" | |
504 | tasks = GetCoalitionTasks(addressof(coal.j.extensions), 1, thread_details) | |
505 | out_string += "\n\t\t".join(tasks) | |
506 | out_string += "\n\t XPC Services:\n\t\t" | |
507 | tasks = GetCoalitionTasks(addressof(coal.j.services), 1, thread_details) | |
508 | out_string += "\n\t\t".join(tasks) | |
509 | out_string += "\n\t Other Tasks:\n\t\t" | |
510 | tasks = GetCoalitionTasks(addressof(coal.j.other), 1, thread_details) | |
511 | out_string += "\n\t\t".join(tasks) | |
512 | out_string += "\n\t Thread Group: {0: <#020x}\n".format(coal.j.thread_group) | |
513 | return out_string | |
514 | ||
515 | @lldb_type_summary(['coalition_t', 'coalition *']) | |
516 | @header("{0: <20s} {1: <15s} {2: <10s} {3: <10s} {4: <10s} {5: <12s} {6: <12s} {7: <20s}".format("coalition", "type", "id", "ref count", "act count", "focal cnt", "nonfocal cnt","flags")) | |
517 | def GetCoalitionSummary(coal): | |
518 | if unsigned(coal) == 0: | |
519 | return '{0: <#020x} {1: <15s} {2: <10d} {3: <10d} {4: <10d} {5: <12d} {6: <12d} {7: <s}'.format(0, "", -1, -1, -1, -1, -1, "") | |
520 | out_string = "" | |
521 | format_string = '{0: <#020x} {1: <15s} {2: <10d} {3: <10d} {4: <10d} {5: <12d} {6: <12d} {7: <s}' | |
522 | type_string = GetCoalitionTypeString(coal.type) | |
523 | flag_string = GetCoalitionFlagString(coal) | |
524 | out_string += format_string.format(coal, type_string, coal.id, coal.ref_count, coal.active_count, coal.focal_task_count, coal.nonfocal_task_count, flag_string) | |
525 | return out_string | |
526 | ||
527 | def GetCoalitionInfo(coal, verbose=False): | |
528 | """ returns a string describing a coalition, including details about the particular coalition type. | |
529 | params: | |
530 | coal : value object representing a coalition in the kernel | |
531 | returns: | |
532 | str : A string describing the coalition. | |
533 | """ | |
534 | if unsigned(coal) == 0: | |
535 | return "<null coalition>" | |
536 | typestr = GetCoalitionTypeString(coal.type) | |
537 | flagstr = GetCoalitionFlagString(coal) | |
538 | out_string = "" | |
539 | out_string += "Coalition {c: <#020x}\n\tID {c.id: <d}\n\tType {c.type: <d} ({t: <s})\n\tRefCount {c.ref_count: <d}\n\tActiveCount {c.active_count: <d}\n\tFocal Tasks: {c.focal_task_count: <d}\n\tNon-Focal Tasks: {c.nonfocal_task_count: <d}\n\tFlags {f: <s}\n\t".format(c=coal,t=typestr,f=flagstr) | |
540 | if coal.type == 0: # COALITION_TYPE_RESOURCE | |
541 | out_string += GetResourceCoalitionSummary(coal, verbose) | |
542 | elif coal.type == 1: # COALITION_TYPE_JETSAM | |
543 | out_string += GetJetsamCoalitionSummary(coal, verbose) | |
544 | else: | |
545 | out_string += "Unknown Type" | |
546 | ||
547 | return out_string | |
548 | ||
549 | # Macro: showcoalitioninfo | |
550 | ||
551 | @lldb_command('showcoalitioninfo') | |
552 | def ShowCoalitionInfo(cmd_args=None, cmd_options={}): | |
553 | """ Display more detailed information about a coalition | |
554 | Usage: showcoalitioninfo <address of coalition> | |
555 | """ | |
556 | verbose = False | |
557 | if config['verbosity'] > vHUMAN: | |
558 | verbose = True | |
559 | if not cmd_args: | |
560 | raise ArgumentError("No arguments passed") | |
561 | coal = kern.GetValueFromAddress(cmd_args[0], 'coalition *') | |
562 | if not coal: | |
563 | print "unknown arguments:", str(cmd_args) | |
564 | return False | |
565 | print GetCoalitionInfo(coal, verbose) | |
566 | ||
567 | # EndMacro: showcoalitioninfo | |
568 | ||
569 | # Macro: showallcoalitions | |
570 | ||
571 | @lldb_command('showallcoalitions') | |
572 | def ShowAllCoalitions(cmd_args=None): | |
573 | """ Print a summary listing of all the coalitions | |
574 | """ | |
575 | global kern | |
576 | print GetCoalitionSummary.header | |
577 | for c in kern.coalitions: | |
578 | print GetCoalitionSummary(c) | |
579 | ||
580 | # EndMacro: showallcoalitions | |
581 | ||
582 | # Macro: showallthreadgroups | |
583 | ||
584 | @lldb_type_summary(['struct thread_group *', 'thread_group *']) | |
585 | @header("{0: <20s} {1: <5s} {2: <16s} {3: <5s} {4: <8s} {5: <20s}".format("thread_group", "id", "name", "refc", "flags", "recommendation")) | |
586 | def GetThreadGroupSummary(tg): | |
587 | if unsigned(tg) == 0: | |
588 | return '{0: <#020x} {1: <5d} {2: <16s} {3: <5d} {4: <8s} {5: <20d}'.format(0, -1, "", -1, "", -1) | |
589 | out_string = "" | |
590 | format_string = '{0: <#020x} {1: <5d} {2: <16s} {3: <5d} {4: <8s} {5: <20d}' | |
591 | tg_flags = '' | |
592 | if (tg.tg_flags & 0x1): | |
593 | tg_flags += 'E' | |
594 | if (tg.tg_flags & 0x2): | |
595 | tg_flags += 'U' | |
596 | out_string += format_string.format(tg, tg.tg_id, tg.tg_name, tg.tg_refcount.ref_count, tg_flags, tg.tg_recommendation) | |
597 | return out_string | |
598 | ||
599 | @lldb_command('showallthreadgroups') | |
600 | def ShowAllThreadGroups(cmd_args=None): | |
601 | """ Print a summary listing of all thread groups | |
602 | """ | |
603 | global kern | |
604 | print GetThreadGroupSummary.header | |
605 | for tg in kern.thread_groups: | |
606 | print GetThreadGroupSummary(tg) | |
607 | ||
608 | # EndMacro: showallthreadgroups | |
609 | ||
610 | # Macro: showtaskcoalitions | |
611 | ||
612 | @lldb_command('showtaskcoalitions', 'F:') | |
613 | def ShowTaskCoalitions(cmd_args=None, cmd_options={}): | |
614 | """ | |
615 | """ | |
616 | task_list = [] | |
617 | if "-F" in cmd_options: | |
618 | task_list = FindTasksByName(cmd_options["-F"]) | |
619 | elif cmd_args: | |
620 | t = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
621 | task_list.append(t) | |
622 | else: | |
623 | raise ArgumentError("No arguments passed") | |
624 | ||
625 | if len(task_list) > 0: | |
626 | print GetCoalitionSummary.header | |
627 | for task in task_list: | |
628 | print GetCoalitionSummary(task.coalition[0]) | |
629 | print GetCoalitionSummary(task.coalition[1]) | |
630 | ||
631 | # EndMacro: showtaskcoalitions | |
632 | ||
633 | @lldb_type_summary(['proc', 'proc *']) | |
634 | @header("{0: >6s} {1: <18s} {2: >11s} {3: ^10s} {4: <32s}".format("pid", "process", "io_policy", "wq_state", "command")) | |
635 | def GetProcSummary(proc): | |
636 | """ Summarize the process data. | |
637 | params: | |
638 | proc : value - value representaitng a proc * in kernel | |
639 | returns: | |
640 | str - string summary of the process. | |
641 | """ | |
642 | out_string = "" | |
643 | format_string= "{0: >6d} {1: <#018x} {2: >11s} {3: >2d} {4: >2d} {5: >2d} {6: <32s}" | |
644 | pval = proc.GetSBValue() | |
645 | #code.interact(local=locals()) | |
646 | if str(pval.GetType()) != str(gettype('proc *')) : | |
647 | return "Unknown type " + str(pval.GetType()) + " " + str(hex(proc)) | |
648 | if not proc: | |
649 | out_string += "Process " + hex(proc) + " is not valid." | |
650 | return out_string | |
651 | pid = int(proc.p_pid) | |
652 | proc_addr = int(hex(proc), 16) | |
653 | proc_rage_str = "" | |
654 | if int(proc.p_lflag) & 0x400000 : | |
655 | proc_rage_str = "RAGE" | |
656 | ||
657 | task = Cast(proc.task, 'task *') | |
658 | ||
659 | io_policy_str = "" | |
660 | ||
661 | if int(task.effective_policy.tep_darwinbg) != 0: | |
662 | io_policy_str += "B" | |
663 | if int(task.effective_policy.tep_lowpri_cpu) != 0: | |
664 | io_policy_str += "L" | |
665 | ||
666 | if int(task.effective_policy.tep_io_tier) != 0: | |
667 | io_policy_str += "T" | |
668 | if int(task.effective_policy.tep_io_passive) != 0: | |
669 | io_policy_str += "P" | |
670 | if int(task.effective_policy.tep_terminated) != 0: | |
671 | io_policy_str += "D" | |
672 | ||
673 | if int(task.effective_policy.tep_latency_qos) != 0: | |
674 | io_policy_str += "Q" | |
675 | if int(task.effective_policy.tep_sup_active) != 0: | |
676 | io_policy_str += "A" | |
677 | ||
678 | ||
679 | try: | |
680 | work_queue = Cast(proc.p_wqptr, 'workqueue *') | |
681 | if proc.p_wqptr != 0 : | |
682 | wq_num_threads = int(work_queue.wq_nthreads) | |
683 | wq_idle_threads = int(work_queue.wq_thidlecount) | |
684 | wq_req_threads = int(work_queue.wq_reqcount) | |
685 | else: | |
686 | wq_num_threads = 0 | |
687 | wq_idle_threads = 0 | |
688 | wq_req_threads = 0 | |
689 | except: | |
690 | wq_num_threads = -1 | |
691 | wq_idle_threads = -1 | |
692 | wq_req_threads = -1 | |
693 | process_name = GetProcName(proc) | |
694 | if process_name == 'xpcproxy': | |
695 | for thread in IterateQueue(task.threads, 'thread *', 'task_threads'): | |
696 | thread_name = GetThreadName(thread) | |
697 | if thread_name: | |
698 | process_name += ' (' + thread_name + ')' | |
699 | break | |
700 | out_string += format_string.format(pid, proc_addr, " ".join([proc_rage_str, io_policy_str]), wq_num_threads, wq_idle_threads, wq_req_threads, process_name) | |
701 | return out_string | |
702 | ||
703 | @lldb_type_summary(['tty_dev_t', 'tty_dev_t *']) | |
704 | @header("{0: <20s} {1: <10s} {2: <10s} {3: <15s} {4: <15s} {5: <15s} {6: <15s}".format("tty_dev","master", "slave", "open", "free", "name", "revoke")) | |
705 | def GetTTYDevSummary(tty_dev): | |
706 | """ Summarizes the important fields in tty_dev_t structure. | |
707 | params: tty_dev: value - value object representing a tty_dev_t in kernel | |
708 | returns: str - summary of the tty_dev | |
709 | """ | |
710 | out_string = "" | |
711 | format_string = "{0: <#020x} {1: <#010x} {2: <#010x} {3: <15s} {4: <15s} {5: <15s} {6: <15s}" | |
712 | open_fn = kern.Symbolicate(int(hex(tty_dev.open), 16)) | |
713 | free_fn = kern.Symbolicate(int(hex(tty_dev.free), 16)) | |
714 | name_fn = kern.Symbolicate(int(hex(tty_dev.name), 16)) | |
715 | revoke_fn = kern.Symbolicate(int(hex(tty_dev.revoke), 16)) | |
716 | out_string += format_string.format(tty_dev, tty_dev.master, tty_dev.slave, open_fn, free_fn, name_fn, revoke_fn) | |
717 | return out_string | |
718 | ||
719 | # Macro: showtask | |
720 | ||
721 | @lldb_command('showtask', 'F:') | |
722 | def ShowTask(cmd_args=None, cmd_options={}): | |
723 | """ Routine to print a summary listing of given task | |
724 | Usage: showtask <address of task> | |
725 | or : showtask -F <name of task> | |
726 | """ | |
727 | task_list = [] | |
728 | if "-F" in cmd_options: | |
729 | task_list = FindTasksByName(cmd_options['-F']) | |
730 | else: | |
731 | if not cmd_args: | |
732 | raise ArgumentError("Invalid arguments passed.") | |
733 | ||
734 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
735 | if not tval: | |
736 | raise ("Unknown arguments: %r" % cmd_args) | |
737 | task_list.append(tval) | |
738 | ||
739 | for tval in task_list: | |
740 | print GetTaskSummary.header + " " + GetProcSummary.header | |
741 | pval = Cast(tval.bsd_info, 'proc *') | |
742 | print GetTaskSummary(tval) +" "+ GetProcSummary(pval) | |
743 | ||
744 | # EndMacro: showtask | |
745 | ||
746 | # Macro: showpid | |
747 | ||
748 | @lldb_command('showpid') | |
749 | def ShowPid(cmd_args=None): | |
750 | """ Routine to print a summary listing of task corresponding to given pid | |
751 | Usage: showpid <pid value> | |
752 | """ | |
753 | if not cmd_args: | |
754 | raise ArgumentError("No arguments passed") | |
755 | pidval = ArgumentStringToInt(cmd_args[0]) | |
756 | for t in kern.tasks: | |
757 | pval = Cast(t.bsd_info, 'proc *') | |
758 | if pval and pval.p_pid == pidval: | |
759 | print GetTaskSummary.header + " " + GetProcSummary.header | |
760 | print GetTaskSummary(t) + " " + GetProcSummary(pval) | |
761 | break | |
762 | ||
763 | # EndMacro: showpid | |
764 | ||
765 | # Macro: showproc | |
766 | ||
767 | @lldb_command('showproc') | |
768 | def ShowProc(cmd_args=None): | |
769 | """ Routine to print a summary listing of task corresponding to given proc | |
770 | Usage: showproc <address of proc> | |
771 | """ | |
772 | if not cmd_args: | |
773 | raise ArgumentError("No arguments passed") | |
774 | pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') | |
775 | if not pval: | |
776 | print "unknown arguments:", str(cmd_args) | |
777 | return False | |
778 | print GetTaskSummary.header + " " + GetProcSummary.header | |
779 | tval = Cast(pval.task, 'task *') | |
780 | print GetTaskSummary(tval) +" "+ GetProcSummary(pval) | |
781 | ||
782 | # EndMacro: showproc | |
783 | ||
784 | # Macro: showprocinfo | |
785 | ||
786 | @lldb_command('showprocinfo') | |
787 | def ShowProcInfo(cmd_args=None): | |
788 | """ Routine to display name, pid, parent & task for the given proc address | |
789 | It also shows the Cred, Flags and state of the process | |
790 | Usage: showprocinfo <address of proc> | |
791 | """ | |
792 | if not cmd_args: | |
793 | raise ArgumentError("No arguments passed") | |
794 | pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') | |
795 | if not pval: | |
796 | print "unknown arguments:", str(cmd_args) | |
797 | return False | |
798 | print GetProcInfo(pval) | |
799 | ||
800 | # EndMacro: showprocinfo | |
801 | ||
802 | #Macro: showprocfiles | |
803 | ||
804 | @lldb_command('showprocfiles') | |
805 | def ShowProcFiles(cmd_args=None): | |
806 | """ Given a proc_t pointer, display the list of open file descriptors for the referenced process. | |
807 | Usage: showprocfiles <proc_t> | |
808 | """ | |
809 | if not cmd_args: | |
810 | print ShowProcFiles.__doc__ | |
811 | return | |
812 | proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') | |
813 | proc_filedesc = proc.p_fd | |
814 | proc_lastfile = unsigned(proc_filedesc.fd_lastfile) | |
815 | proc_ofiles = proc_filedesc.fd_ofiles | |
816 | if unsigned(proc_ofiles) == 0: | |
817 | print 'No open files for proc {0: <s}'.format(cmd_args[0]) | |
818 | return | |
819 | print "{0: <5s} {1: <18s} {2: <10s} {3: <8s} {4: <18s} {5: <64s}".format('FD', 'FILEGLOB', 'FG_FLAGS', 'FG_TYPE', 'FG_DATA','INFO') | |
820 | print "{0:-<5s} {0:-<18s} {0:-<10s} {0:-<8s} {0:-<18s} {0:-<64s}".format("") | |
821 | count = 0 | |
822 | ||
823 | while count <= proc_lastfile: | |
824 | if unsigned(proc_ofiles[count]) != 0: | |
825 | out_str = '' | |
826 | proc_fd_flags = proc_ofiles[count].fp_flags | |
827 | proc_fd_fglob = proc_ofiles[count].fp_glob | |
828 | out_str += "{0: <5d} ".format(count) | |
829 | out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob)) | |
830 | out_str += "0x{0:0>8x} ".format(unsigned(proc_fd_flags)) | |
831 | proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type) | |
832 | if proc_fd_ftype in xnudefines.filetype_strings: | |
833 | out_str += "{0: <8s} ".format(xnudefines.filetype_strings[proc_fd_ftype]) | |
834 | else: | |
835 | out_str += "?: {0: <5d} ".format(proc_fd_ftype) | |
836 | out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob.fg_data)) | |
837 | if proc_fd_ftype == 1: | |
838 | fd_name = Cast(proc_fd_fglob.fg_data, 'struct vnode *').v_name | |
839 | out_str += "{0: <64s}".format(fd_name) | |
840 | out_str += "\n" | |
841 | print out_str | |
842 | count += 1 | |
843 | ||
844 | #EndMacro: showprocfiles | |
845 | ||
846 | #Macro: showtty | |
847 | ||
848 | @lldb_command('showtty') | |
849 | def ShowTTY(cmd_args=None): | |
850 | """ Display information about a struct tty | |
851 | Usage: showtty <tty struct> | |
852 | """ | |
853 | if not cmd_args: | |
854 | print ShowTTY.__doc__ | |
855 | return | |
856 | ||
857 | tty = kern.GetValueFromAddress(cmd_args[0], 'struct tty *') | |
858 | print "TTY structure at: {0: <s}".format(cmd_args[0]) | |
859 | print "Last input to raw queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_rawq.c_cs), tty.t_rawq.c_cs) | |
860 | print "Last input to canonical queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_canq.c_cs), tty.t_canq.c_cs) | |
861 | print "Last output data: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_outq.c_cs), tty.t_outq.c_cs) | |
862 | tty_state_info = [ | |
863 | ['', 'TS_SO_OLOWAT (Wake up when output <= low water)'], | |
864 | ['- (synchronous I/O mode)', 'TS_ASYNC (async I/O mode)'], | |
865 | ['', 'TS_BUSY (Draining output)'], | |
866 | ['- (Carrier is NOT present)', 'TS_CARR_ON (Carrier is present)'], | |
867 | ['', 'TS_FLUSH (Outq has been flushed during DMA)'], | |
868 | ['- (Open has NOT completed)', 'TS_ISOPEN (Open has completed)'], | |
869 | ['', 'TS_TBLOCK (Further input blocked)'], | |
870 | ['', 'TS_TIMEOUT (Wait for output char processing)'], | |
871 | ['', 'TS_TTSTOP (Output paused)'], | |
872 | ['', 'TS_WOPEN (Open in progress)'], | |
873 | ['', 'TS_XCLUDE (Tty requires exclusivity)'], | |
874 | ['', 'TS_BKSL (State for lowercase \\ work)'], | |
875 | ['', 'TS_CNTTB (Counting tab width, ignore FLUSHO)'], | |
876 | ['', 'TS_ERASE (Within a \\.../ for PRTRUB)'], | |
877 | ['', 'TS_LNCH (Next character is literal)'], | |
878 | ['', 'TS_TYPEN (Retyping suspended input (PENDIN))'], | |
879 | ['', 'TS_CAN_BYPASS_L_RINT (Device in "raw" mode)'], | |
880 | ['- (Connection NOT open)', 'TS_CONNECTED (Connection open)'], | |
881 | ['', 'TS_SNOOP (Device is being snooped on)'], | |
882 | ['', 'TS_SO_OCOMPLETE (Wake up when output completes)'], | |
883 | ['', 'TS_ZOMBIE (Connection lost)'], | |
884 | ['', 'TS_CAR_OFLOW (For MDMBUF - handle in driver)'], | |
885 | ['', 'TS_CTS_OFLOW (For CCTS_OFLOW - handle in driver)'], | |
886 | ['', 'TS_DSR_OFLOW (For CDSR_OFLOW - handle in driver)'] | |
887 | ] | |
888 | index = 0 | |
889 | mask = 0x1 | |
890 | tty_state = unsigned(tty.t_state) | |
891 | print "State:" | |
892 | while index < 24: | |
893 | if tty_state & mask != 0: | |
894 | if len(tty_state_info[index][1]) > 0: | |
895 | print '\t' + tty_state_info[index][1] | |
896 | else: | |
897 | if len(tty_state_info[index][0]) > 0: | |
898 | print '\t' + tty_state_info[index][0] | |
899 | index += 1 | |
900 | mask = mask << 1 | |
901 | print "Flags: 0x{0:0>8x}".format(unsigned(tty.t_flags)) | |
902 | print "Foreground Process Group: 0x{0:0>16x}".format(unsigned(tty.t_pgrp)) | |
903 | print "Enclosing session: 0x{0:0>16x}".format(unsigned(tty.t_session)) | |
904 | print "Termios:" | |
905 | print "\tInput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_iflag)) | |
906 | print "\tOutput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_oflag)) | |
907 | print "\tControl Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_cflag)) | |
908 | print "\tLocal Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_lflag)) | |
909 | print "\tInput Speed: {0: <8d}".format(tty.t_termios.c_ispeed) | |
910 | print "\tOutput Speed: {0: <8d}".format(tty.t_termios.c_ospeed) | |
911 | print "High Watermark: {0: <d} bytes".format(tty.t_hiwat) | |
912 | print "Low Watermark : {0: <d} bytes".format(tty.t_lowat) | |
913 | ||
914 | #EndMacro: showtty | |
915 | ||
916 | #Macro showallttydevs | |
917 | ||
918 | @lldb_command('showallttydevs') | |
919 | def ShowAllTTYDevs(cmd_args=[], cmd_options={}): | |
920 | """ Show a list of ttydevs registered in the system. | |
921 | Usage: | |
922 | (lldb)showallttydevs | |
923 | """ | |
924 | tty_dev_head = kern.globals.tty_dev_head | |
925 | tty_dev = tty_dev_head | |
926 | print GetTTYDevSummary.header | |
927 | while unsigned(tty_dev) != 0: | |
928 | print GetTTYDevSummary(tty_dev) | |
929 | tty_dev = tty_dev.next | |
930 | return "" | |
931 | ||
932 | #EndMacro: showallttydevs | |
933 | ||
934 | #Macro: dumpthread_terminate_queue | |
935 | ||
936 | @lldb_command('dumpthread_terminate_queue') | |
937 | def DumpThreadTerminateQueue(cmd_args=None): | |
938 | """ Displays the contents of the specified call_entry queue. | |
939 | Usage: dumpthread_terminate_queue | |
940 | """ | |
941 | ||
942 | count = 0 | |
943 | print GetThreadSummary.header | |
944 | for th in IterateMPSCQueue(addressof(kern.globals.thread_terminate_queue.mpd_queue), 'struct thread', 'mpsc_links'): | |
945 | print GetThreadSummary(th) | |
946 | count += 1 | |
947 | print "{0: <d} entries!".format(count) | |
948 | ||
949 | #EndMacro: dumpthread_terminate_queue | |
950 | ||
951 | #Macro: dumpcrashed_thread_queue | |
952 | ||
953 | @lldb_command('dumpcrashed_thread_queue') | |
954 | def DumpCrashedThreadsQueue(cmd_args=None): | |
955 | """ Displays the contents of the specified call_entry queue. | |
956 | Usage: dumpcrashed_thread_queue | |
957 | """ | |
958 | ||
959 | count = 0 | |
960 | print GetThreadSummary.header | |
961 | for th in IterateQueue(addressof(kern.globals.crashed_threads_queue), 'struct thread *', 'q_link'): | |
962 | print GetThreadSummary(th) | |
963 | count += 1 | |
964 | print "{0: <d} entries!".format(count) | |
965 | ||
966 | #EndMacro: dumpcrashed_thread_queue | |
967 | ||
968 | #Macro: dumpcallqueue | |
969 | ||
970 | @lldb_command('dumpcallqueue') | |
971 | def DumpCallQueue(cmd_args=None): | |
972 | """ Displays the contents of the specified call_entry queue. | |
973 | Usage: dumpcallqueue <queue_head_t *> | |
974 | """ | |
975 | if not cmd_args: | |
976 | raise ArgumentError("Invalid arguments") | |
977 | ||
978 | print "{0: <18s} {1: <18s} {2: <18s} {3: <64s} {4: <18s}".format('CALL_ENTRY', 'PARAM0', 'PARAM1', 'DEADLINE', 'FUNC') | |
979 | callhead = kern.GetValueFromAddress(cmd_args[0], 'queue_head_t *') | |
980 | count = 0 | |
981 | for callentry in IterateQueue(callhead, 'struct call_entry *', 'q_link'): | |
982 | print "{0: <#18x} {1: <#18x} {2: <#18x} {3: <64d} {4: <#18x}".format( | |
983 | unsigned(callentry), unsigned(callentry.param0), unsigned(callentry.param1), | |
984 | unsigned(callentry.deadline), unsigned(callentry.func)) | |
985 | count += 1 | |
986 | print "{0: <d} entries!".format(count) | |
987 | ||
988 | #EndMacro: dumpcallqueue | |
989 | ||
990 | @lldb_command('showalltasklogicalwrites') | |
991 | def ShowAllTaskIOStats(cmd_args=None): | |
992 | """ Commad to print I/O stats for all tasks | |
993 | """ | |
994 | print "{0: <20s} {1: <20s} {2: <20s} {3: <20s} {4: <20s} {5: <20s} {6: <20s} {7: <20s} {8: <20s} {9: <32}".format("task", "Immediate Writes", "Deferred Writes", "Invalidated Writes", "Metadata Writes", "Immediate Writes to External", "Deferred Writes to External", "Invalidated Writes to External", "Metadata Writes to External", "name") | |
995 | for t in kern.tasks: | |
996 | pval = Cast(t.bsd_info, 'proc *') | |
997 | print "{0: <#18x} {1: >20d} {2: >20d} {3: >20d} {4: >20d} {5: <20s} {6: <20s} {7: <20s} {8: <20s} {9: <20s}".format(t, | |
998 | t.task_writes_counters_internal.task_immediate_writes, | |
999 | t.task_writes_counters_internal.task_deferred_writes, | |
1000 | t.task_writes_counters_internal.task_invalidated_writes, | |
1001 | t.task_writes_counters_internal.task_metadata_writes, | |
1002 | t.task_writes_counters_external.task_immediate_writes, | |
1003 | t.task_writes_counters_external.task_deferred_writes, | |
1004 | t.task_writes_counters_external.task_invalidated_writes, | |
1005 | t.task_writes_counters_external.task_metadata_writes, | |
1006 | GetProcName(pval)) | |
1007 | ||
1008 | ||
1009 | @lldb_command('showalltasks','C', fancy=True) | |
1010 | def ShowAllTasks(cmd_args=None, cmd_options={}, O=None): | |
1011 | """ Routine to print a summary listing of all the tasks | |
1012 | wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" | |
1013 | if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung | |
1014 | io_policy -> RAGE - rapid aging of vnodes requested | |
1015 | NORM - normal I/O explicitly requested (this is the default) | |
1016 | PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) | |
1017 | THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) | |
1018 | Usage: (lldb) showalltasks -C : describe the corpse structure | |
1019 | """ | |
1020 | global kern | |
1021 | extra_hdr = '' | |
1022 | showcorpse = False | |
1023 | if '-C' in cmd_options: | |
1024 | showcorpse = True | |
1025 | extra_hdr += " " + GetKCDataSummary.header | |
1026 | ||
1027 | with O.table(GetTaskSummary.header + extra_hdr + " " + GetProcSummary.header): | |
1028 | for t in kern.tasks: | |
1029 | pval = Cast(t.bsd_info, 'proc *') | |
1030 | print GetTaskSummary(t, showcorpse) + " " + GetProcSummary(pval) | |
1031 | ||
1032 | ZombTasks() | |
1033 | ||
1034 | @lldb_command('taskforpmap') | |
1035 | def TaskForPmap(cmd_args=None): | |
1036 | """ Find the task whose pmap corresponds to <pmap>. | |
1037 | Syntax: (lldb) taskforpmap <pmap> | |
1038 | Multiple -v's can be specified for increased verbosity | |
1039 | """ | |
1040 | if cmd_args == None or len(cmd_args) < 1: | |
1041 | raise ArgumentError("Too few arguments to taskforpmap.") | |
1042 | pmap = kern.GetValueFromAddress(cmd_args[0], 'pmap_t') | |
1043 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1044 | for tasklist in [kern.tasks, kern.terminated_tasks]: | |
1045 | for t in tasklist: | |
1046 | if kern.GetValueFromAddress(unsigned(t.map.pmap), 'pmap_t') == pmap: | |
1047 | pval = Cast(t.bsd_info, 'proc *') | |
1048 | out_str = GetTaskSummary(t) + " " + GetProcSummary(pval) | |
1049 | print out_str | |
1050 | ||
1051 | @lldb_command('showterminatedtasks') | |
1052 | def ShowTerminatedTasks(cmd_args=None): | |
1053 | """ Routine to print a summary listing of all the terminated tasks | |
1054 | wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" | |
1055 | if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung | |
1056 | io_policy -> RAGE - rapid aging of vnodes requested | |
1057 | NORM - normal I/O explicitly requested (this is the default) | |
1058 | PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) | |
1059 | THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) | |
1060 | syntax: (lldb)showallterminatedtasks | |
1061 | """ | |
1062 | global kern | |
1063 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1064 | for t in kern.terminated_tasks: | |
1065 | ||
1066 | # If the task has been terminated it's likely that the process is | |
1067 | # gone too. If there is no proc it may still be possible to find | |
1068 | # the original proc name. | |
1069 | pval = Cast(t.bsd_info, 'proc *') | |
1070 | if pval: | |
1071 | psummary = GetProcSummary(pval) | |
1072 | else: | |
1073 | name = GetProcNameForTask(t); | |
1074 | pslen = GetProcSummary.header.find("command"); | |
1075 | psummary = "{0: <{indent}} {1: <s}".format("", name, indent = pslen - 1) | |
1076 | ||
1077 | print GetTaskSummary(t) + " " + psummary | |
1078 | ||
1079 | return True | |
1080 | ||
1081 | # Macro: showtaskstacks | |
1082 | ||
1083 | def ShowTaskStacks(task): | |
1084 | """ Print a task with summary and stack information for each of its threads | |
1085 | """ | |
1086 | global kern | |
1087 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1088 | pval = Cast(task.bsd_info, 'proc *') | |
1089 | print GetTaskSummary(task) + " " + GetProcSummary(pval) | |
1090 | for th in IterateQueue(task.threads, 'thread *', 'task_threads'): | |
1091 | print " " + GetThreadSummary.header | |
1092 | print " " + GetThreadSummary(th) | |
1093 | print GetThreadBackTrace(th, prefix=" ") + "\n" | |
1094 | ||
1095 | def FindTasksByName(searchstr, ignore_case=True): | |
1096 | """ Search the list of tasks by name. | |
1097 | params: | |
1098 | searchstr: str - a regex like string to search for task | |
1099 | ignore_case: bool - If False then exact matching will be enforced | |
1100 | returns: | |
1101 | [] - array of task object. Empty if not found any | |
1102 | """ | |
1103 | re_options = 0 | |
1104 | if ignore_case: | |
1105 | re_options = re.IGNORECASE | |
1106 | search_regex = re.compile(searchstr, re_options) | |
1107 | retval = [] | |
1108 | for t in kern.tasks: | |
1109 | pval = Cast(t.bsd_info, "proc *") | |
1110 | process_name = "{:s}".format(GetProcName(pval)) | |
1111 | if search_regex.search(process_name): | |
1112 | retval.append(t) | |
1113 | return retval | |
1114 | ||
1115 | @lldb_command('showtaskstacks', 'F:') | |
1116 | def ShowTaskStacksCmdHelper(cmd_args=None, cmd_options={}): | |
1117 | """ Routine to print out the stack for each thread in a task | |
1118 | Usage: showtaskstacks <0xaddress of task> | |
1119 | or: showtaskstacks -F launchd | |
1120 | """ | |
1121 | ||
1122 | if "-F" in cmd_options: | |
1123 | find_task_str = cmd_options["-F"] | |
1124 | task_list = FindTasksByName(find_task_str) | |
1125 | for tval in task_list: | |
1126 | ShowTaskStacks(tval) | |
1127 | return | |
1128 | ||
1129 | if not cmd_args: | |
1130 | raise ArgumentError("No arguments passed") | |
1131 | ||
1132 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
1133 | if not tval: | |
1134 | raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) | |
1135 | else: | |
1136 | ShowTaskStacks(tval) | |
1137 | return | |
1138 | ||
1139 | # EndMacro: showtaskstacks | |
1140 | ||
1141 | def CheckTaskProcRefs(task, proc): | |
1142 | for thread in IterateQueue(task.threads, 'thread *', 'task_threads'): | |
1143 | if int(thread.uthread) == 0: | |
1144 | continue | |
1145 | uthread = Cast(thread.uthread, 'uthread *') | |
1146 | refcount = int(uthread.uu_proc_refcount) | |
1147 | uu_ref_index = int(uthread.uu_pindex) | |
1148 | if refcount == 0: | |
1149 | continue | |
1150 | for ref in range(0, uu_ref_index): | |
1151 | if unsigned(uthread.uu_proc_ps[ref]) == unsigned(proc): | |
1152 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1153 | pval = Cast(task.bsd_info, 'proc *') | |
1154 | print GetTaskSummary(task) + " " + GetProcSummary(pval) | |
1155 | print "\t" + GetThreadSummary.header | |
1156 | print "\t" + GetThreadSummary(thread) + "\n" | |
1157 | ||
1158 | for frame in range (0, 10): | |
1159 | trace_addr = unsigned(uthread.uu_proc_pcs[ref][frame]) | |
1160 | symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr)) | |
1161 | if symbol_arr: | |
1162 | symbol_str = str(symbol_arr[0].addr) | |
1163 | else: | |
1164 | symbol_str = '' | |
1165 | print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str) | |
1166 | return | |
1167 | ||
1168 | @lldb_command('showprocrefs') | |
1169 | def ShowProcRefs(cmd_args = None): | |
1170 | """ Display information on threads/BTs that could be holding a reference on the specified proc | |
1171 | NOTE: We can't say affirmatively if any of these references are still held since | |
1172 | there's no way to pair references with drop-refs in the current infrastructure. | |
1173 | Usage: showprocrefs <proc> | |
1174 | """ | |
1175 | if cmd_args == None or len(cmd_args) < 1: | |
1176 | raise ArgumentError("No arguments passed") | |
1177 | ||
1178 | proc = kern.GetValueFromAddress(cmd_args[0], 'proc *') | |
1179 | ||
1180 | for t in kern.tasks: | |
1181 | CheckTaskProcRefs(t, proc) | |
1182 | for t in kern.terminated_tasks: | |
1183 | CheckTaskProcRefs(t, proc) | |
1184 | ||
1185 | return | |
1186 | ||
1187 | @lldb_command('showallthreads') | |
1188 | def ShowAllThreads(cmd_args = None): | |
1189 | """ Display info about all threads in the system | |
1190 | """ | |
1191 | ||
1192 | # Terminated threads get prefixed with a 'T' | |
1193 | def ShowTaskTerminatedThreads(task): | |
1194 | tlist = tmap.get(unsigned(task), []) | |
1195 | for thval in tlist: | |
1196 | print "T\t" + GetThreadSummary(thval) | |
1197 | ||
1198 | # Task -> [thread, ..] map of terminated threads | |
1199 | tmap = defaultdict(list) | |
1200 | for thr in kern.terminated_threads: | |
1201 | tmap[unsigned(thr.task)].append(thr) | |
1202 | ||
1203 | for t in kern.tasks: | |
1204 | ShowTaskThreads([str(int(t))]) | |
1205 | ShowTaskTerminatedThreads(t) | |
1206 | print " \n" | |
1207 | ||
1208 | for t in kern.terminated_tasks: | |
1209 | print "Terminated: \n" | |
1210 | ShowTaskThreads([str(int(t))]) | |
1211 | ShowTaskTerminatedThreads(t) | |
1212 | print " \n" | |
1213 | ||
1214 | return | |
1215 | ||
1216 | @lldb_command('showterminatedthreads') | |
1217 | def ShowTerminatedThreads(cmd_args=None): | |
1218 | """ Display info about all terminated threads in the system | |
1219 | """ | |
1220 | ||
1221 | global kern | |
1222 | print GetThreadSummary.header | |
1223 | for t in kern.terminated_threads: | |
1224 | print GetThreadSummary(t) | |
1225 | ||
1226 | return | |
1227 | ||
1228 | @lldb_command('showtaskthreads', "F:") | |
1229 | def ShowTaskThreads(cmd_args = None, cmd_options={}): | |
1230 | """ Display thread information for a given task | |
1231 | Usage: showtaskthreads <0xaddress of task> | |
1232 | or: showtaskthreads -F <name> | |
1233 | """ | |
1234 | task_list = [] | |
1235 | if "-F" in cmd_options: | |
1236 | task_list = FindTasksByName(cmd_options["-F"]) | |
1237 | elif cmd_args: | |
1238 | t = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
1239 | task_list.append(t) | |
1240 | else: | |
1241 | raise ArgumentError("No arguments passed") | |
1242 | ||
1243 | for task in task_list: | |
1244 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1245 | pval = Cast(task.bsd_info, 'proc *') | |
1246 | print GetTaskSummary(task) + " " + GetProcSummary(pval) | |
1247 | print "\t" + GetThreadSummary.header | |
1248 | for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): | |
1249 | print "\t" + GetThreadSummary(thval) | |
1250 | return | |
1251 | ||
1252 | @lldb_command('showact') | |
1253 | def ShowAct(cmd_args=None): | |
1254 | """ Routine to print out the state of a specific thread. | |
1255 | usage: showact <activation> | |
1256 | """ | |
1257 | if not cmd_args: | |
1258 | raise ArgumentError("No arguments passed") | |
1259 | threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') | |
1260 | print GetThreadSummary.header | |
1261 | print GetThreadSummary(threadval) | |
1262 | ||
1263 | @lldb_command('showactstack') | |
1264 | def ShowActStack(cmd_args=None): | |
1265 | """ Routine to print out the stack of a specific thread. | |
1266 | usage: showactstack <activation> | |
1267 | """ | |
1268 | if not cmd_args: | |
1269 | raise ArgumentError("No arguments passed") | |
1270 | threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') | |
1271 | print GetThreadSummary.header | |
1272 | print GetThreadSummary(threadval) | |
1273 | print GetThreadBackTrace(threadval, prefix="\t") | |
1274 | return | |
1275 | ||
1276 | @lldb_command('switchtoact') | |
1277 | def SwitchToAct(cmd_args=None): | |
1278 | """ Switch to different context specified by activation | |
1279 | This command allows gdb to examine the execution context and call | |
1280 | stack for the specified activation. For example, to view the backtrace | |
1281 | for an activation issue "switchtoact <address>", followed by "bt". | |
1282 | Before resuming execution, issue a "resetctx" command, to | |
1283 | return to the original execution context. | |
1284 | """ | |
1285 | if cmd_args is None or len(cmd_args) < 1: | |
1286 | raise ArgumentError("No arguments passed") | |
1287 | thval = kern.GetValueFromAddress(cmd_args[0], 'thread *') | |
1288 | lldbthread = GetLLDBThreadForKernelThread(thval) | |
1289 | print GetThreadSummary.header | |
1290 | print GetThreadSummary(thval) | |
1291 | LazyTarget.GetProcess().selected_thread = lldbthread | |
1292 | if not LazyTarget.GetProcess().SetSelectedThread(lldbthread): | |
1293 | print "Failed to switch thread." | |
1294 | return | |
1295 | ||
1296 | @lldb_command('switchtoregs') | |
1297 | def SwitchToRegs(cmd_args=None): | |
1298 | """ Routine to switch to a register state. | |
1299 | Usage: (lldb) switchtoregs <struct arm_saved_state[64] *> | |
1300 | This command creates a fake thread in lldb with the saved register state. | |
1301 | Note: This command ONLY works for ARM based kernel setup. | |
1302 | """ | |
1303 | ||
1304 | if cmd_args == None or len(cmd_args) < 1: | |
1305 | raise ArgumentError("No arguments passed") | |
1306 | ||
1307 | lldb_process = LazyTarget.GetProcess() | |
1308 | ||
1309 | saved_state = ArgumentStringToInt(cmd_args[0]) | |
1310 | # any change to this logic requires change in operating_system.py as well | |
1311 | fake_thread_id = 0xdead0000 | (saved_state & ~0xffff0000) | |
1312 | fake_thread_id = fake_thread_id & 0xdeadffff | |
1313 | lldb_process.CreateOSPluginThread(0xdeadbeef, saved_state) | |
1314 | lldbthread = lldb_process.GetThreadByID(int(fake_thread_id)) | |
1315 | ||
1316 | if not lldbthread.IsValid(): | |
1317 | print "Failed to create thread" | |
1318 | return | |
1319 | ||
1320 | lldb_process.selected_thread = lldbthread | |
1321 | if not lldb_process.SetSelectedThread(lldbthread): | |
1322 | print "Failed to switch thread" | |
1323 | print "Switched to Fake thread created from register state at 0x%x" % saved_state | |
1324 | ||
1325 | ||
1326 | ||
1327 | # Macro: showallstacks | |
1328 | @lldb_command('showallstacks') | |
1329 | def ShowAllStacks(cmd_args=None): | |
1330 | """Routine to print out the stack for each thread in the system. | |
1331 | """ | |
1332 | for t in kern.tasks: | |
1333 | ShowTaskStacks(t) | |
1334 | print " \n" | |
1335 | ZombStacks() | |
1336 | return | |
1337 | ||
1338 | # EndMacro: showallstacks | |
1339 | ||
1340 | # Macro: showcurrentstacks | |
1341 | @lldb_command('showcurrentstacks') | |
1342 | def ShowCurrentStacks(cmd_args=None): | |
1343 | """ Routine to print out the thread running on each cpu (incl. its stack) | |
1344 | """ | |
1345 | processor_list = kern.GetGlobalVariable('processor_list') | |
1346 | current_processor = processor_list | |
1347 | while unsigned(current_processor) > 0: | |
1348 | print "\n" + GetProcessorSummary(current_processor) | |
1349 | active_thread = current_processor.active_thread | |
1350 | if unsigned(active_thread) != 0 : | |
1351 | task_val = active_thread.task | |
1352 | proc_val = Cast(task_val.bsd_info, 'proc *') | |
1353 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1354 | print GetTaskSummary(task_val) + " " + GetProcSummary(proc_val) | |
1355 | print "\t" + GetThreadSummary.header | |
1356 | print "\t" + GetThreadSummary(active_thread) | |
1357 | print "\tBacktrace:" | |
1358 | print GetThreadBackTrace(active_thread, prefix="\t") | |
1359 | current_processor = current_processor.processor_list | |
1360 | return | |
1361 | # EndMacro: showcurrentstacks | |
1362 | ||
1363 | @lldb_command('showcurrentthreads') | |
1364 | def ShowCurrentThreads(cmd_args=None): | |
1365 | """ Display info about threads running on each cpu """ | |
1366 | processor_list = kern.GetGlobalVariable('processor_list') | |
1367 | current_processor = processor_list | |
1368 | while unsigned(current_processor) > 0: | |
1369 | print GetProcessorSummary(current_processor) | |
1370 | active_thread = current_processor.active_thread | |
1371 | if unsigned(active_thread) != 0 : | |
1372 | task_val = active_thread.task | |
1373 | proc_val = Cast(task_val.bsd_info, 'proc *') | |
1374 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1375 | print GetTaskSummary(task_val) + " " + GetProcSummary(proc_val) | |
1376 | print "\t" + GetThreadSummary.header | |
1377 | print "\t" + GetThreadSummary(active_thread) | |
1378 | current_processor = current_processor.processor_list | |
1379 | return | |
1380 | ||
1381 | def GetFullBackTrace(frame_addr, verbosity = vHUMAN, prefix = ""): | |
1382 | """ Get backtrace across interrupt context. | |
1383 | params: frame_addr - int - address in memory which is a frame pointer (ie. rbp, r7) | |
1384 | prefix - str - prefix for each line of output. | |
1385 | ||
1386 | """ | |
1387 | out_string = "" | |
1388 | bt_count = 0 | |
1389 | frame_ptr = frame_addr | |
1390 | previous_frame_ptr = 0 | |
1391 | # <rdar://problem/12677290> lldb unable to find symbol for _mh_execute_header | |
1392 | mh_execute_addr = int(lldb_run_command('p/x (uintptr_t *)&_mh_execute_header').split('=')[-1].strip(), 16) | |
1393 | while frame_ptr and frame_ptr != previous_frame_ptr and bt_count < 128: | |
1394 | if (not kern.arch.startswith('arm') and frame_ptr < mh_execute_addr) or (kern.arch.startswith('arm') and frame_ptr > mh_execute_addr): | |
1395 | break | |
1396 | pc_val = kern.GetValueFromAddress(frame_ptr + kern.ptrsize,'uintptr_t *') | |
1397 | pc_val = kern.StripKernelPAC(unsigned(dereference(pc_val))) | |
1398 | out_string += prefix + GetSourceInformationForAddress(pc_val) + "\n" | |
1399 | bt_count +=1 | |
1400 | previous_frame_ptr = frame_ptr | |
1401 | frame_val = kern.GetValueFromAddress((frame_ptr), 'uintptr_t *') | |
1402 | if unsigned(frame_val) == 0: | |
1403 | break | |
1404 | frame_ptr = unsigned(dereference(frame_val)) | |
1405 | ||
1406 | return out_string | |
1407 | ||
1408 | @lldb_command('fullbt') | |
1409 | def FullBackTrace(cmd_args=[]): | |
1410 | """ Show full backtrace across the interrupt boundary. | |
1411 | Syntax: fullbt <frame ptr> | |
1412 | Example: fullbt `$rbp` | |
1413 | """ | |
1414 | if len(cmd_args) < 1: | |
1415 | print FullBackTrace.__doc__ | |
1416 | return False | |
1417 | print GetFullBackTrace(ArgumentStringToInt(cmd_args[0]), prefix="\t") | |
1418 | ||
1419 | @lldb_command('fullbtall') | |
1420 | def FullBackTraceAll(cmd_args=[]): | |
1421 | """ Show full backtrace across the interrupt boundary for threads running on all processors. | |
1422 | Syntax: fullbtall | |
1423 | Example: fullbtall | |
1424 | """ | |
1425 | for processor in IterateLinkedList(kern.globals.processor_list, 'processor_list') : | |
1426 | print "\n" + GetProcessorSummary(processor) | |
1427 | active_thread = processor.active_thread | |
1428 | if unsigned(active_thread) != 0 : | |
1429 | task_val = active_thread.task | |
1430 | proc_val = Cast(task_val.bsd_info, 'proc *') | |
1431 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1432 | print GetTaskSummary(task_val) + " " + GetProcSummary(proc_val) | |
1433 | print "\t" + GetThreadSummary.header | |
1434 | print "\t" + GetThreadSummary(active_thread) | |
1435 | print "\tBacktrace:" | |
1436 | ||
1437 | ThreadVal = GetLLDBThreadForKernelThread(active_thread) | |
1438 | ||
1439 | FramePtr = ThreadVal.frames[0].GetFP() | |
1440 | ||
1441 | print GetFullBackTrace(unsigned(FramePtr), prefix="\t") | |
1442 | ||
1443 | ||
1444 | @lldb_command('symbolicate') | |
1445 | def SymbolicateAddress(cmd_args=[]): | |
1446 | """ Symbolicate an address for symbol information from loaded symbols | |
1447 | Example: "symbolicate 0xaddr" is equivalent to "output/a 0xaddr" | |
1448 | """ | |
1449 | if len(cmd_args) < 1: | |
1450 | print "Invalid address.\nSyntax: symbolicate <address>" | |
1451 | return False | |
1452 | print GetSourceInformationForAddress(ArgumentStringToInt(cmd_args[0])) | |
1453 | return True | |
1454 | ||
1455 | @lldb_command('showinitchild') | |
1456 | def ShowInitChild(cmd_args=None): | |
1457 | """ Routine to print out all processes in the system | |
1458 | which are children of init process | |
1459 | """ | |
1460 | headp = kern.globals.initproc.p_children | |
1461 | for pp in IterateListEntry(headp, 'struct proc *', 'p_sibling'): | |
1462 | print GetProcInfo(pp) | |
1463 | return | |
1464 | ||
1465 | @lldb_command('showproctree') | |
1466 | def ShowProcTree(cmd_args=None): | |
1467 | """ Routine to print the processes in the system in a hierarchical tree form. This routine does not print zombie processes. | |
1468 | If no argument is given, showproctree will print all the processes in the system. | |
1469 | If pid is specified, showproctree prints all the descendants of the indicated process | |
1470 | """ | |
1471 | search_pid = 0 | |
1472 | if cmd_args: | |
1473 | search_pid = ArgumentStringToInt(cmd_args[0]) | |
1474 | ||
1475 | if search_pid < 0: | |
1476 | print "pid specified must be a positive number" | |
1477 | print ShowProcTree.__doc__ | |
1478 | return | |
1479 | ||
1480 | hdr_format = "{0: <6s} {1: <14s} {2: <9s}\n" | |
1481 | out_string = hdr_format.format("PID", "PROCESS", "POINTER") | |
1482 | out_string += hdr_format.format('='*3, '='*7, '='*7) | |
1483 | proc = GetProcForPid(search_pid) | |
1484 | out_string += "{0: <6d} {1: <32s} [ {2: #019x} ]\n".format(proc.p_ppid, GetProcName(proc.p_pptr), unsigned(proc.p_pptr)) | |
1485 | out_string += "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format(proc.p_pid, GetProcName(proc), unsigned(proc)) | |
1486 | print out_string | |
1487 | ShowProcTreeRecurse(proc, "| ") | |
1488 | ||
1489 | return | |
1490 | ||
1491 | def ShowProcTreeRecurse(proc, prefix=""): | |
1492 | """ Prints descendants of a given proc in hierarchial tree form | |
1493 | params: | |
1494 | proc : core.value representing a struct proc * in the kernel | |
1495 | returns: | |
1496 | str : String containing info about a given proc and its descendants in tree form | |
1497 | """ | |
1498 | if proc.p_childrencnt > 0: | |
1499 | head_ptr = proc.p_children.lh_first | |
1500 | ||
1501 | for p in IterateListEntry(proc.p_children, 'struct proc *', 'p_sibling'): | |
1502 | print prefix + "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format(p.p_pid, GetProcName(p), unsigned(p)) | |
1503 | ShowProcTreeRecurse(p, prefix + "| ") | |
1504 | ||
1505 | @lldb_command('showthreadfortid') | |
1506 | def ShowThreadForTid(cmd_args=None): | |
1507 | """ The thread structure contains a unique thread_id value for each thread. | |
1508 | This command is used to retrieve the address of the thread structure(thread_t) | |
1509 | corresponding to a given thread_id. | |
1510 | """ | |
1511 | if not cmd_args: | |
1512 | print "Please provide thread_t whose tid you'd like to look up" | |
1513 | print ShowThreadForTid.__doc__ | |
1514 | return | |
1515 | search_tid = ArgumentStringToInt(cmd_args[0]) | |
1516 | for taskp in kern.tasks: | |
1517 | for actp in IterateQueue(taskp.threads, 'struct thread *', 'task_threads'): | |
1518 | if search_tid == int(actp.thread_id): | |
1519 | print "Found {0: #019x}".format(actp) | |
1520 | print GetThreadSummary.header | |
1521 | print GetThreadSummary(actp) | |
1522 | return | |
1523 | print "Not a valid thread_id" | |
1524 | ||
1525 | def GetProcessorSummary(processor): | |
1526 | """ Internal function to print summary of processor | |
1527 | params: processor - value representing struct processor * | |
1528 | return: str - representing the details of given processor | |
1529 | """ | |
1530 | ||
1531 | processor_state_str = "INVALID" | |
1532 | processor_state = int(processor.state) | |
1533 | ||
1534 | processor_states = { | |
1535 | 0: 'OFF_LINE', | |
1536 | 1: 'SHUTDOWN', | |
1537 | 2: 'START', | |
1538 | # 3 (formerly INACTIVE) | |
1539 | 4: 'IDLE', | |
1540 | 5: 'DISPATCHING', | |
1541 | 6: 'RUNNING' | |
1542 | } | |
1543 | ||
1544 | if processor_state in processor_states: | |
1545 | processor_state_str = "{0: <11s} ".format(processor_states[processor_state]) | |
1546 | ||
1547 | processor_recommended_str = "" | |
1548 | if int(processor.is_recommended) == 0: | |
1549 | processor_recommended_str = " (not recommended)" | |
1550 | ||
1551 | ast = 0 | |
1552 | preemption_disable = 0 | |
1553 | preemption_disable_str = "" | |
1554 | ||
1555 | if kern.arch == 'x86_64': | |
1556 | cpu_data = kern.globals.cpu_data_ptr[processor.cpu_id] | |
1557 | if (cpu_data != 0) : | |
1558 | ast = cpu_data.cpu_pending_ast | |
1559 | preemption_disable = cpu_data.cpu_preemption_level | |
1560 | # On arm64, it's kern.globals.CpuDataEntries[processor.cpu_id].cpu_data_vaddr | |
1561 | # but LLDB can't find CpuDataEntries... | |
1562 | ||
1563 | ast_str = GetASTSummary(ast) | |
1564 | ||
1565 | if (preemption_disable != 0) : | |
1566 | preemption_disable_str = "Preemption Disabled" | |
1567 | ||
1568 | out_str = "Processor {: <#018x} cpu_id {:>#4x} AST: {:<6s} State {:<s}{:<s} {:<s}\n".format( | |
1569 | processor, int(processor.cpu_id), ast_str, processor_state_str, processor_recommended_str, | |
1570 | preemption_disable_str) | |
1571 | return out_str | |
1572 | ||
1573 | def GetLedgerEntry(ledger_template, ledger, i): | |
1574 | """ Internal function to get internals of a ledger entry (*not* a ledger itself) | |
1575 | params: ledger_template - value representing struct ledger_template_t for the task or thread | |
1576 | ledger - value representing struct ledger_entry * | |
1577 | return: entry - entry dictionary | |
1578 | """ | |
1579 | ledger_limit_infinity = (uint64_t(0x1).value << 63) - 1 | |
1580 | lf_refill_scheduled = 0x0400 | |
1581 | lf_tracking_max = 0x4000 | |
1582 | ||
1583 | now = unsigned(kern.globals.sched_tick) / 20 | |
1584 | lim_pct = 0 | |
1585 | ||
1586 | entry = {} | |
1587 | ||
1588 | entry["key"] = str(ledger_template.lt_entries[i].et_key) | |
1589 | entry["credit"] = unsigned(ledger.le_credit) | |
1590 | entry["debit"] = unsigned(ledger.le_debit) | |
1591 | entry["balance"] = entry["credit"] - entry["debit"] | |
1592 | if (ledger.le_flags & lf_tracking_max): | |
1593 | entry["interval_max"] = unsigned(ledger._le._le_max.le_interval_max) | |
1594 | entry["lifetime_max"] = unsigned(ledger._le._le_max.le_lifetime_max) | |
1595 | ||
1596 | if (unsigned(ledger.le_limit) != ledger_limit_infinity): | |
1597 | entry["limit"] = unsigned(ledger.le_limit) | |
1598 | ||
1599 | if (ledger.le_flags & lf_refill_scheduled): | |
1600 | entry["refill_period"] = unsigned (ledger._le.le_refill.le_refill_period) | |
1601 | ||
1602 | if (unsigned(ledger.le_warn_percent) < 65535): | |
1603 | entry["warn_percent"] = unsigned (ledger.le_warn_percent * 100 / 65536) | |
1604 | entry["flags"] = int(ledger.le_flags) | |
1605 | ||
1606 | return entry | |
1607 | ||
1608 | def FormatLedgerEntrySummary(entry, i, show_footprint_interval_max=False): | |
1609 | """ internal function to format a ledger entry into a string | |
1610 | params: entry - A python dictionary containing the ledger entry | |
1611 | return: str - formatted output information of ledger entries | |
1612 | """ | |
1613 | out_str = '' | |
1614 | out_str += "{: >32s} {:<2d}:".format(entry["key"], i) | |
1615 | out_str += "{: >15d} ".format(entry["balance"]) | |
1616 | ||
1617 | if (show_footprint_interval_max): | |
1618 | if entry.has_key("interval_max"): | |
1619 | out_str += "{:12d} ".format(entry["interval_max"]) | |
1620 | else: | |
1621 | out_str += " - " | |
1622 | ||
1623 | if entry.has_key("lifetime_max"): | |
1624 | out_str += "{:14d} ".format(entry["lifetime_max"]) | |
1625 | else: | |
1626 | out_str += " - " | |
1627 | ||
1628 | out_str += "{:12d} {:12d} ".format(entry["credit"], entry["debit"]) | |
1629 | if entry.has_key("limit"): | |
1630 | out_str += "{:12d} ".format(unsigned(entry["limit"])) | |
1631 | else: | |
1632 | out_str += " - " | |
1633 | ||
1634 | if entry.has_key("refill_period"): | |
1635 | out_str += "{:15d} ".format(entry["refill_period"]) | |
1636 | out_str += "{:9d} ".format((entry["limit"] * 100) / entry["refill_period"]) | |
1637 | else: | |
1638 | out_str += " - " | |
1639 | out_str += " - " | |
1640 | ||
1641 | if entry.has_key("warn_percent"): | |
1642 | out_str += "{:9d} ".format(entry["warn_percent"]) | |
1643 | else: | |
1644 | out_str += " - " | |
1645 | ||
1646 | if entry.has_key("limit"): | |
1647 | if entry["balance"] > entry["limit"]: | |
1648 | out_str += " X " | |
1649 | else: | |
1650 | out_str += " " | |
1651 | else: | |
1652 | out_str += " " | |
1653 | ||
1654 | out_str += "{:#8x}\n".format(entry["flags"]) | |
1655 | return out_str | |
1656 | ||
1657 | def GetLedgerEntrySummary(ledger_template, ledger, i, show_footprint_interval_max=False): | |
1658 | """ internal function to get internals of a ledger entry (*not* a ledger itself) | |
1659 | params: ledger_template - value representing struct ledger_template_t for the task or thread | |
1660 | ledger - value representing struct ledger_entry * | |
1661 | return: str - formatted output information of ledger entries | |
1662 | """ | |
1663 | entry = GetLedgerEntry(ledger_template, ledger, i) | |
1664 | return FormatLedgerEntrySummary(entry, i) | |
1665 | ||
1666 | ||
1667 | def GetThreadLedgers(thread_val): | |
1668 | """ Internal function to get a summary of ledger entries for the given thread | |
1669 | params: thread_val - value representing struct thread * | |
1670 | return: thread - python dictionary containing threads's ledger entries. This can | |
1671 | be printed directly with FormatThreadLedgerSummmary or outputted as json. | |
1672 | """ | |
1673 | thread = {} | |
1674 | thread["address"] = unsigned(thread_val) | |
1675 | ledgerp = thread_val.t_threadledger | |
1676 | thread["entries"] = [] | |
1677 | if ledgerp: | |
1678 | i = 0 | |
1679 | while i != ledgerp.l_template.lt_cnt: | |
1680 | thread["entries"].append(GetLedgerEntry(kern.globals.thread_ledger_template, | |
1681 | ledgerp.l_entries[i], i)) | |
1682 | i = i + 1 | |
1683 | return thread | |
1684 | ||
1685 | def FormatThreadLedgerSummary(thread): | |
1686 | """ Internal function to print a thread's ledger entries | |
1687 | params: thread - python dictionary containing thread's ledger entries | |
1688 | return: str - formatted output information for ledger entries of the input thread | |
1689 | """ | |
1690 | out_str = " [{:#08x}]\n".format(thread["address"]) | |
1691 | entries = thread["entries"] | |
1692 | for i, entry in enumerate(entries): | |
1693 | out_str += FormatLedgerEntrySummary(entry, i) | |
1694 | return out_str | |
1695 | ||
1696 | def GetTaskLedgers(task_val): | |
1697 | """ Internal function to get summary of ledger entries from the task and its threads | |
1698 | params: task_val - value representing struct task * | |
1699 | return: task - python dictionary containing tasks's ledger entries. This can | |
1700 | be printed directly with FormatTaskLedgerSummary or outputted as json. | |
1701 | """ | |
1702 | task_ledgerp = task_val.ledger | |
1703 | i = 0 | |
1704 | tasks = [] | |
1705 | task = {} | |
1706 | task["address"] = unsigned(task_val) | |
1707 | ||
1708 | pval = Cast(task_val.bsd_info, 'proc *') | |
1709 | if pval: | |
1710 | task["name"] = GetProcName(pval) | |
1711 | task["pid"] = int(pval.p_pid) | |
1712 | ||
1713 | task["entries"] = [] | |
1714 | while i != task_ledgerp.l_template.lt_cnt: | |
1715 | task["entries"].append(GetLedgerEntry(kern.globals.task_ledger_template, task_ledgerp.l_entries[i], i)) | |
1716 | i = i + 1 | |
1717 | ||
1718 | # Now walk threads | |
1719 | task["threads"] = [] | |
1720 | for thval in IterateQueue(task_val.threads, 'thread *', 'task_threads'): | |
1721 | task["threads"].append(GetThreadLedgers(thval)) | |
1722 | ||
1723 | return task | |
1724 | ||
1725 | @header("{0: <15s} {1: >16s} {2: <2s} {3: >15s} {4: >14s} {5: >12s} {6: >12s} {7: >12s} {8: <15s} {9: <8s} {10: <9s} {11: <6s} {12: >6s}".format( | |
1726 | "task [thread]", "entry", "#", "balance", "lifetime_max", "credit", | |
1727 | "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags")) | |
1728 | def FormatTaskLedgerSummary(task, show_footprint_interval_max=False): | |
1729 | """ Internal function to get summary of ledger entries from the task and its threads | |
1730 | params: task_val - value representing struct task * | |
1731 | return: str - formatted output information for ledger entries of the input task | |
1732 | """ | |
1733 | out_str = '' | |
1734 | out_str += "{: #08x} ".format(task["address"]) | |
1735 | if task.has_key("name"): | |
1736 | out_str += "{: <5s}:\n".format(task["name"]) | |
1737 | else: | |
1738 | out_str += "Invalid process\n" | |
1739 | ||
1740 | for i, entry in enumerate(task["entries"]): | |
1741 | out_str += FormatLedgerEntrySummary(entry, i, show_footprint_interval_max) | |
1742 | ||
1743 | for thread in task["threads"]: | |
1744 | out_str += FormatThreadLedgerSummary(thread) | |
1745 | return out_str | |
1746 | ||
1747 | ||
1748 | # Macro: showtaskledgers | |
1749 | ||
1750 | @lldb_command('showtaskledgers', 'JF:I') | |
1751 | def ShowTaskLedgers(cmd_args=None, cmd_options={}): | |
1752 | """ Routine to print a summary of ledger entries for the task and all of its threads | |
1753 | or : showtaskledgers [ -I ] [-J] [ -F ] <task> | |
1754 | options: | |
1755 | -I: show footprint interval max (DEV/DEBUG only) | |
1756 | -F: specify task via name instead of address | |
1757 | -J: output json | |
1758 | - | |
1759 | """ | |
1760 | print_json = False | |
1761 | if "-F" in cmd_options: | |
1762 | task_list = FindTasksByName(cmd_options["-F"]) | |
1763 | for tval in task_list: | |
1764 | print FormatTaskLedgerSummary.header | |
1765 | ledgers = GetTaskLedgers(tval) | |
1766 | print FormatTaskLedgerSummary(ledgers) | |
1767 | return | |
1768 | if "-J" in cmd_options: | |
1769 | print_json = True | |
1770 | ||
1771 | if not cmd_args: | |
1772 | raise ArgumentError("No arguments passed.") | |
1773 | show_footprint_interval_max = False | |
1774 | if "-I" in cmd_options: | |
1775 | show_footprint_interval_max = True | |
1776 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
1777 | if not tval: | |
1778 | raise ArgumentError("unknown arguments: %r" %cmd_args) | |
1779 | ledgers = GetTaskLedgers(tval) | |
1780 | if print_json: | |
1781 | print json.dumps(ledgers) | |
1782 | else: | |
1783 | if (show_footprint_interval_max): | |
1784 | print "{0: <15s} {1: >16s} {2: <2s} {3: >15s} {4: >12s} {5: >14s} {6: >12s} {7: >12s} {8: >12s} {9: <15s} {10: <8s} {11: <9s} {12: <6s} {13: >6s}".format( | |
1785 | "task [thread]", "entry", "#", "balance", "intrvl_max", "lifetime_max", "credit", | |
1786 | "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags") | |
1787 | else: | |
1788 | print FormatTaskLedgerSummary.header | |
1789 | print FormatTaskLedgerSummary(ledgers, show_footprint_interval_max) | |
1790 | ||
1791 | # EndMacro: showtaskledgers | |
1792 | ||
1793 | # Macro: showalltaskledgers | |
1794 | ||
1795 | @lldb_command('showalltaskledgers', "J") | |
1796 | def ShowAllTaskLedgers(cmd_args=None, cmd_options={}): | |
1797 | """ Routine to print a summary of ledger entries for all tasks and respective threads | |
1798 | Usage: showalltaskledgers [-J] | |
1799 | -J : Output json | |
1800 | """ | |
1801 | print_json = False | |
1802 | if "-J" in cmd_options: | |
1803 | print_json = True | |
1804 | tasks = [] | |
1805 | for t in kern.tasks: | |
1806 | task_val = unsigned(t) | |
1807 | if not print_json: | |
1808 | ShowTaskLedgers([task_val], cmd_options=cmd_options) | |
1809 | else: | |
1810 | tasks.append(GetTaskLedgers(t)) | |
1811 | if print_json: | |
1812 | print json.dumps(tasks) | |
1813 | ||
1814 | # EndMacro: showalltaskledgers | |
1815 | ||
1816 | # Macro: showprocuuidpolicytable | |
1817 | ||
1818 | @lldb_type_summary(['proc_uuid_policy_entry']) | |
1819 | @header("{0: <36s} {1: <10s}".format("uuid", "flags")) | |
1820 | def GetProcUUIDPolicyEntrySummary(entry): | |
1821 | """ Summarizes the important fields in proc_uuid_policy_entry structure. | |
1822 | params: entry: value - value object representing an entry | |
1823 | returns: str - summary of the entry | |
1824 | """ | |
1825 | data = [] | |
1826 | for i in range(16): | |
1827 | data.append(int(entry.uuid[i])) | |
1828 | flags = unsigned(entry.flags) | |
1829 | out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X} 0x{b:0>8x}".format(a=data, b=flags) | |
1830 | return out_string | |
1831 | ||
1832 | @lldb_command('showprocuuidpolicytable') | |
1833 | def ShowProcUUIDPolicyTable(cmd_args=None): | |
1834 | """ Routine to print the proc UUID policy table | |
1835 | Usage: showprocuuidpolicytable | |
1836 | """ | |
1837 | hashslots = unsigned(kern.globals.proc_uuid_policy_hash_mask) | |
1838 | print "{0: <8s} ".format("slot") + GetProcUUIDPolicyEntrySummary.header | |
1839 | for i in range(0, hashslots+1): | |
1840 | headp = addressof(kern.globals.proc_uuid_policy_hashtbl[i]) | |
1841 | entrynum = 0 | |
1842 | for entry in IterateListEntry(headp, 'struct proc_uuid_policy_entry *', 'entries'): | |
1843 | print "{0: >2d}.{1: <5d} ".format(i, entrynum) + GetProcUUIDPolicyEntrySummary(entry) | |
1844 | entrynum += 1 | |
1845 | ||
1846 | ||
1847 | # EndMacro: showprocuuidpolicytable | |
1848 | ||
1849 | @lldb_command('showalltaskpolicy') | |
1850 | def ShowAllTaskPolicy(cmd_args=None): | |
1851 | """ | |
1852 | Routine to print a summary listing of all the tasks | |
1853 | wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" | |
1854 | if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung | |
1855 | io_policy -> RAGE - rapid aging of vnodes requested | |
1856 | NORM - normal I/O explicitly requested (this is the default) | |
1857 | PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) | |
1858 | THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) | |
1859 | """ | |
1860 | global kern | |
1861 | print GetTaskSummary.header + " " + GetProcSummary.header | |
1862 | for t in kern.tasks: | |
1863 | pval = Cast(t.bsd_info, 'proc *') | |
1864 | print GetTaskSummary(t) +" "+ GetProcSummary(pval) | |
1865 | requested_strings = [ | |
1866 | ["int_darwinbg", "DBG-int"], | |
1867 | ["ext_darwinbg", "DBG-ext"], | |
1868 | ["int_iotier", "iotier-int"], | |
1869 | ["ext_iotier", "iotier-ext"], | |
1870 | ["int_iopassive", "passive-int"], | |
1871 | ["ext_iopassive", "passive-ext"], | |
1872 | ["bg_iotier", "bg-iotier"], | |
1873 | ["terminated", "terminated"], | |
1874 | ["th_pidbind_bg", "bg-pidbind"], | |
1875 | ["t_apptype", "apptype"], | |
1876 | ["t_boosted", "boosted"], | |
1877 | ["t_role", "role"], | |
1878 | ["t_tal_enabled", "tal-enabled"], | |
1879 | ["t_base_latency_qos", "latency-base"], | |
1880 | ["t_over_latency_qos", "latency-override"], | |
1881 | ["t_base_through_qos", "throughput-base"], | |
1882 | ["t_over_through_qos", "throughput-override"] | |
1883 | ] | |
1884 | ||
1885 | requested="" | |
1886 | for value in requested_strings: | |
1887 | if t.requested_policy.__getattr__(value[0]) : | |
1888 | requested+=value[1] + ": " + str(t.requested_policy.__getattr__(value[0])) + " " | |
1889 | else: | |
1890 | requested+="" | |
1891 | ||
1892 | suppression_strings = [ | |
1893 | ["t_sup_active", "active"], | |
1894 | ["t_sup_lowpri_cpu", "lowpri-cpu"], | |
1895 | ["t_sup_timer", "timer-throttling"], | |
1896 | ["t_sup_disk", "disk-throttling"], | |
1897 | ["t_sup_cpu_limit", "cpu-limits"], | |
1898 | ["t_sup_suspend", "suspend"], | |
1899 | ["t_sup_bg_sockets", "bg-sockets"] | |
1900 | ] | |
1901 | ||
1902 | suppression="" | |
1903 | for value in suppression_strings: | |
1904 | if t.requested_policy.__getattr__(value[0]) : | |
1905 | suppression+=value[1] + ": " + str(t.requested_policy.__getattr__(value[0])) + " " | |
1906 | else: | |
1907 | suppression+="" | |
1908 | ||
1909 | effective_strings = [ | |
1910 | ["darwinbg", "background"], | |
1911 | ["lowpri_cpu", "lowpri-cpu"], | |
1912 | ["io_tier", "iotier"], | |
1913 | ["io_passive", "passive"], | |
1914 | ["all_sockets_bg", "bg-allsockets"], | |
1915 | ["new_sockets_bg", "bg-newsockets"], | |
1916 | ["bg_iotier", "bg-iotier"], | |
1917 | ["terminated", "terminated"], | |
1918 | ["t_gpu_deny", "gpu-deny"], | |
1919 | ["t_tal_engaged", "tal-engaged"], | |
1920 | ["t_suspended", "suspended"], | |
1921 | ["t_watchers_bg", "bg-watchers"], | |
1922 | ["t_latency_qos", "latency-qos"], | |
1923 | ["t_through_qos", "throughput-qos"], | |
1924 | ["t_sup_active", "suppression-active"], | |
1925 | ["t_role", "role"] | |
1926 | ] | |
1927 | ||
1928 | effective="" | |
1929 | for value in effective_strings: | |
1930 | if t.effective_policy.__getattr__(value[0]) : | |
1931 | effective+=value[1] + ": " + str(t.effective_policy.__getattr__(value[0])) + " " | |
1932 | else: | |
1933 | effective+="" | |
1934 | ||
1935 | print "requested: " + requested | |
1936 | print "suppression: " + suppression | |
1937 | print "effective: " + effective | |
1938 | ||
1939 | ||
1940 | @lldb_type_summary(['wait_queue', 'wait_queue_t']) | |
1941 | @header("{: <20s} {: <20s} {: <15s} {:<5s} {:<5s} {: <20s}".format("waitq", "interlock", "policy", "members", "threads", "eventmask")) | |
1942 | def GetWaitQSummary(waitq): | |
1943 | """ Summarizes the important fields in task structure. | |
1944 | params: task: value - value object representing a task in kernel | |
1945 | returns: str - summary of the task | |
1946 | """ | |
1947 | out_string = "" | |
1948 | format_string = '{: <#020x} {: <#020x} {: <15s} {: <5d} {: <5d} {: <#020x}' | |
1949 | ||
1950 | wqtype = "" | |
1951 | ||
1952 | if (waitq.wq_fifo == 1) : | |
1953 | wqtype += "FIFO" | |
1954 | else : | |
1955 | wqtype += "PRIO" | |
1956 | ||
1957 | if (waitq.wq_prepost == 1) : | |
1958 | wqtype += "Prepost" | |
1959 | ||
1960 | if (waitq.wq_type == 0x3) : | |
1961 | wqtype += "Set" | |
1962 | elif (waitq.wq_type == 0x2) : | |
1963 | wqtype += "Queue" | |
1964 | else : | |
1965 | wqtype += "INVALID" | |
1966 | ||
1967 | out_string += format_string.format(waitq, unsigned(waitq.wq_interlock.lock_data), policy, 0, 0, unsigned(waitq.wq_eventmask)) | |
1968 | ||
1969 | out_string += "\n" + GetThreadSummary.header | |
1970 | ||
1971 | for thread in IterateQueue(waitq.wq_queue, "thread_t", "links"): | |
1972 | out_string += "\n" + GetThreadSummary(thread) | |
1973 | ||
1974 | return out_string | |
1975 | ||
1976 | ||
1977 | @lldb_command('showallsuspendedtasks', '') | |
1978 | def ShowSuspendedTasks(cmd_args=[], options={}): | |
1979 | """ Show a list of suspended tasks with their process name summary. | |
1980 | """ | |
1981 | print GetTaskSummary.header + ' ' + GetProcSummary.header | |
1982 | for t in kern.tasks: | |
1983 | if t.suspend_count > 0: | |
1984 | print GetTaskSummary(t) + ' ' + GetProcSummary(Cast(t.bsd_info, 'proc *')) | |
1985 | return True | |
1986 | ||
1987 | # Macro: showallpte | |
1988 | @lldb_command('showallpte') | |
1989 | def ShowAllPte(cmd_args=None): | |
1990 | """ Prints out the physical address of the pte for all tasks | |
1991 | """ | |
1992 | head_taskp = addressof(kern.globals.tasks) | |
1993 | taskp = Cast(head_taskp.next, 'task *') | |
1994 | while taskp != head_taskp: | |
1995 | procp = Cast(taskp.bsd_info, 'proc *') | |
1996 | out_str = "task = {:#x} pte = {:#x}\t".format(taskp, taskp.map.pmap.ttep) | |
1997 | if procp != 0: | |
1998 | out_str += "{:s}\n".format(GetProcName(procp)) | |
1999 | else: | |
2000 | out_str += "\n" | |
2001 | print out_str | |
2002 | taskp = Cast(taskp.tasks.next, 'struct task *') | |
2003 | ||
2004 | # EndMacro: showallpte | |
2005 | ||
2006 | # Macro: showallrefcounts | |
2007 | @lldb_command('showallrefcounts') | |
2008 | @header("{0: <20s} {1: ^10s}".format("task", "ref_count")) | |
2009 | def ShowAllRefCounts(cmd_args=None): | |
2010 | """ Prints the ref_count of all tasks | |
2011 | """ | |
2012 | out_str = '' | |
2013 | head_taskp = addressof(kern.globals.tasks) | |
2014 | taskp = Cast(head_taskp.next, 'task *') | |
2015 | print ShowAllRefCounts.header | |
2016 | while taskp != head_taskp: | |
2017 | out_str += "{: <#20x}".format(taskp) | |
2018 | out_str += "{: ^10d}\n".format(taskp.ref_count) | |
2019 | taskp = Cast(taskp.tasks.next, 'task *') | |
2020 | print out_str | |
2021 | # EndMacro: showallrefcounts | |
2022 | ||
2023 | # Macro: showallrunnablethreads | |
2024 | @lldb_command('showallrunnablethreads') | |
2025 | def ShowAllRunnableThreads(cmd_args=None): | |
2026 | """ Prints the sched usage information for all threads of each task | |
2027 | """ | |
2028 | out_str = '' | |
2029 | for taskp in kern.tasks: | |
2030 | for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): | |
2031 | if int(actp.state & 0x4): | |
2032 | ShowActStack([unsigned(actp)]) | |
2033 | ||
2034 | # EndMacro: showallrunnablethreads | |
2035 | ||
2036 | # Macro: showallschedusage | |
2037 | @lldb_command('showallschedusage') | |
2038 | @header("{0:<20s} {1:^10s} {2:^10s} {3:^15s}".format("Thread", "Priority", "State", "sched_usage")) | |
2039 | def ShowAllSchedUsage(cmd_args=None): | |
2040 | """ Prints the sched usage information for all threads of each task | |
2041 | """ | |
2042 | out_str = '' | |
2043 | for taskp in kern.tasks: | |
2044 | ShowTask([unsigned(taskp)]) | |
2045 | print ShowAllSchedUsage.header | |
2046 | for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): | |
2047 | out_str = "{: <#20x}".format(actp) | |
2048 | out_str += "{: ^10s}".format(str(int(actp.sched_pri))) | |
2049 | state = int(actp.state) | |
2050 | thread_state_chars = {0:'', 1:'W', 2:'S', 4:'R', 8:'U', 16:'H', 32:'A', 64:'P', 128:'I'} | |
2051 | state_str = '' | |
2052 | state_str += thread_state_chars[int(state & 0x1)] | |
2053 | state_str += thread_state_chars[int(state & 0x2)] | |
2054 | state_str += thread_state_chars[int(state & 0x4)] | |
2055 | state_str += thread_state_chars[int(state & 0x8)] | |
2056 | state_str += thread_state_chars[int(state & 0x10)] | |
2057 | state_str += thread_state_chars[int(state & 0x20)] | |
2058 | state_str += thread_state_chars[int(state & 0x40)] | |
2059 | state_str += thread_state_chars[int(state & 0x80)] | |
2060 | out_str += "{: ^10s}".format(state_str) | |
2061 | out_str += "{: >15d}".format(actp.sched_usage) | |
2062 | print out_str + "\n" | |
2063 | print "\n\n" | |
2064 | ||
2065 | # EndMacro: showallschedusage | |
2066 | ||
2067 | #Macro: showprocfilessummary | |
2068 | @lldb_command('showprocfilessummary') | |
2069 | @header("{0: <20s} {1: <20s} {2: >10s}".format("Process", "Name", "Number of Open Files")) | |
2070 | def ShowProcFilesSummary(cmd_args=None): | |
2071 | """ Display the summary of open file descriptors for all processes in task list | |
2072 | Usage: showprocfilessummary | |
2073 | """ | |
2074 | print ShowProcFilesSummary.header | |
2075 | for proc in kern.procs: | |
2076 | proc_filedesc = proc.p_fd | |
2077 | proc_ofiles = proc_filedesc.fd_ofiles | |
2078 | proc_lastfile = unsigned(proc_filedesc.fd_lastfile) | |
2079 | count = 0 | |
2080 | proc_file_count = 0 | |
2081 | if proc_filedesc.fd_nfiles != 0: | |
2082 | while count <= proc_lastfile: | |
2083 | if unsigned(proc_ofiles[count]) != 0: | |
2084 | proc_file_count += 1 | |
2085 | count += 1 | |
2086 | print "{0: <#020x} {1: <32s} {2: >10d}".format(proc, GetProcName(proc), proc_file_count) | |
2087 | ||
2088 | #EndMacro: showprocfilessummary | |
2089 | ||
2090 | @lldb_command('workinguserstacks') | |
2091 | def WorkingUserStacks(cmd_args=None): | |
2092 | """ Print out the user stack for each thread in a task, followed by the user libraries. | |
2093 | Syntax: (lldb) workinguserstacks <task_t> | |
2094 | """ | |
2095 | if not cmd_args: | |
2096 | print "Insufficient arguments" + ShowTaskUserStacks.__doc__ | |
2097 | return False | |
2098 | task = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
2099 | print GetTaskSummary.header + " " + GetProcSummary.header | |
2100 | pval = Cast(task.bsd_info, 'proc *') | |
2101 | print GetTaskSummary(task) + " " + GetProcSummary(pval) + "\n \n" | |
2102 | for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): | |
2103 | print "For thread 0x{0:x}".format(thval) | |
2104 | try: | |
2105 | ShowThreadUserStack([hex(thval)]) | |
2106 | except Exception as exc_err: | |
2107 | print "Failed to show user stack for thread 0x{0:x}".format(thval) | |
2108 | if config['debug']: | |
2109 | raise exc_err | |
2110 | else: | |
2111 | print "Enable debugging ('(lldb) xnudebug debug') to see detailed trace." | |
2112 | WorkingUserLibraries([hex(task)]) | |
2113 | return | |
2114 | ||
2115 | @static_var("exec_load_path", 0) | |
2116 | @lldb_command("workingkuserlibraries") | |
2117 | def WorkingUserLibraries(cmd_args=None): | |
2118 | """ Show binary images known by dyld in target task | |
2119 | For a given user task, inspect the dyld shared library state and print information about all Mach-O images. | |
2120 | Syntax: (lldb)workinguserlibraries <task_t> | |
2121 | """ | |
2122 | if not cmd_args: | |
2123 | print "Insufficient arguments" | |
2124 | print ShowTaskUserLibraries.__doc__ | |
2125 | return False | |
2126 | ||
2127 | print "{0: <18s} {1: <12s} {2: <36s} {3: <50s}".format('address','type','uuid','path') | |
2128 | out_format = "0x{0:0>16x} {1: <12s} {2: <36s} {3: <50s}" | |
2129 | task = kern.GetValueFromAddress(cmd_args[0], 'task_t') | |
2130 | is_task_64 = int(task.t_flags) & 0x1 | |
2131 | dyld_all_image_infos_address = unsigned(task.all_image_info_addr) | |
2132 | cur_data_offset = 0 | |
2133 | if dyld_all_image_infos_address == 0: | |
2134 | print "No dyld shared library information available for task" | |
2135 | return False | |
2136 | vers_info_data = GetUserDataAsString(task, dyld_all_image_infos_address, 112) | |
2137 | version = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") | |
2138 | cur_data_offset += 4 | |
2139 | if version > 12: | |
2140 | print "Unknown dyld all_image_infos version number %d" % version | |
2141 | image_info_count = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") | |
2142 | WorkingUserLibraries.exec_load_path = 0 | |
2143 | if is_task_64: | |
2144 | image_info_size = 24 | |
2145 | image_info_array_address = _ExtractDataFromString(vers_info_data, 8, "uint64_t") | |
2146 | dyld_load_address = _ExtractDataFromString(vers_info_data, 8*4, "uint64_t") | |
2147 | dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 8*13, "uint64_t") | |
2148 | else: | |
2149 | image_info_size = 12 | |
2150 | image_info_array_address = _ExtractDataFromString(vers_info_data, 4*2, "uint32_t") | |
2151 | dyld_load_address = _ExtractDataFromString(vers_info_data, 4*5, "uint32_t") | |
2152 | dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 4*14, "uint32_t") | |
2153 | # Account for ASLR slide before dyld can fix the structure | |
2154 | dyld_load_address = dyld_load_address + (dyld_all_image_infos_address - dyld_all_image_infos_address_from_struct) | |
2155 | ||
2156 | i = 0 | |
2157 | while i < image_info_count: | |
2158 | image_info_address = image_info_array_address + i * image_info_size | |
2159 | img_data = GetUserDataAsString(task, image_info_address, image_info_size) | |
2160 | if is_task_64: | |
2161 | image_info_addr = _ExtractDataFromString(img_data, 0, "uint64_t") | |
2162 | image_info_path = _ExtractDataFromString(img_data, 8, "uint64_t") | |
2163 | else: | |
2164 | image_info_addr = _ExtractDataFromString(img_data, 0, "uint32_t") | |
2165 | image_info_path = _ExtractDataFromString(img_data, 4, "uint32_t") | |
2166 | PrintImageInfo(task, image_info_addr, image_info_path) | |
2167 | i += 1 | |
2168 | ||
2169 | # load_path might get set when the main executable is processed. | |
2170 | if WorkingUserLibraries.exec_load_path != 0: | |
2171 | PrintImageInfo(task, dyld_load_address, WorkingUserLibraries.exec_load_path) | |
2172 | return | |
2173 | ||
2174 | # Macro: showstackaftertask | |
2175 | @lldb_command('showstackaftertask','F:') | |
2176 | def Showstackaftertask(cmd_args=None,cmd_options={}): | |
2177 | """ Routine to print the thread stacks for all tasks succeeding a given task | |
2178 | Usage: showstackaftertask <0xaddress of task> | |
2179 | or: showstackaftertask -F <taskname> | |
2180 | """ | |
2181 | if "-F" in cmd_options: | |
2182 | # Find the task pointer corresponding to its task name | |
2183 | find_task_str = cmd_options["-F"] | |
2184 | task_list = FindTasksByName(find_task_str) | |
2185 | ||
2186 | # Iterate through the list of tasks and print all task stacks thereafter | |
2187 | for tval in task_list: | |
2188 | ListTaskStacks(tval) | |
2189 | return | |
2190 | ||
2191 | if not cmd_args: | |
2192 | raise ArgumentError("Insufficient arguments") | |
2193 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
2194 | if not tval: | |
2195 | raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) | |
2196 | else: | |
2197 | ListTaskStacks(tval) | |
2198 | ||
2199 | ZombStacks() | |
2200 | return | |
2201 | # EndMacro: showstackaftertask | |
2202 | ||
2203 | def ListTaskStacks(task): | |
2204 | """ Search for a given task and print the list of all task stacks thereafter. | |
2205 | """ | |
2206 | # Initialize local variable task_flag to mark when a given task is found. | |
2207 | task_flag=0 | |
2208 | ||
2209 | for t in kern.tasks: | |
2210 | if (task_flag == 1): | |
2211 | ShowTaskStacks(t) | |
2212 | print "\n" | |
2213 | if (t == task): | |
2214 | task_flag = 1 | |
2215 | ||
2216 | # Macro: showstackafterthread | |
2217 | @lldb_command('showstackafterthread') | |
2218 | def Showstackafterthread(cmd_args = None): | |
2219 | """ Routine to print the stacks of all threads succeeding a given thread. | |
2220 | Usage: Showstackafterthread <0xaddress of thread> | |
2221 | """ | |
2222 | # local variable thread_flag is used to mark when a given thread is found. | |
2223 | thread_flag=0 | |
2224 | if cmd_args: | |
2225 | threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') | |
2226 | else: | |
2227 | raise ArgumentError("No arguments passed") | |
2228 | # Iterate through list of all tasks to look up a given thread | |
2229 | for t in kern.tasks: | |
2230 | if(thread_flag==1): | |
2231 | pval = Cast(t.bsd_info, 'proc *') | |
2232 | print GetTaskSummary.header + " "+ GetProcSummary.header | |
2233 | print GetTaskSummary(t) + " "+ GetProcSummary(pval) | |
2234 | print "\n" | |
2235 | # Look up for a given thread from the the list of threads of a given task | |
2236 | for thval in IterateQueue(t.threads, 'thread *', 'task_threads'): | |
2237 | if (thread_flag==1): | |
2238 | print "\n" | |
2239 | print " " + GetThreadSummary.header | |
2240 | print " " + GetThreadSummary(thval) | |
2241 | print GetThreadBackTrace(thval, prefix="\t")+"\n" | |
2242 | print "\n" | |
2243 | ||
2244 | if(thval==threadval): | |
2245 | pval = Cast(t.bsd_info, 'proc *') | |
2246 | process_name = "{:s}".format(GetProcName(pval)) | |
2247 | print "\n\n" | |
2248 | print " *** Continuing to dump the thread stacks from the process *** :" + " " + process_name | |
2249 | print "\n\n" | |
2250 | thread_flag = 1 | |
2251 | print '\n' | |
2252 | return | |
2253 |