X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/a1c7dba18ef36983396c282fe85292db066e39db..ecc0ceb4089d506a0b8d16686a95817b331af9cb:/tools/lldbmacros/process.py?ds=sidebyside diff --git a/tools/lldbmacros/process.py b/tools/lldbmacros/process.py index 40f8cfedb..e2ddb8e3d 100644 --- a/tools/lldbmacros/process.py +++ b/tools/lldbmacros/process.py @@ -1,12 +1,13 @@ """ Please make sure you read the README file COMPLETELY BEFORE reading anything below. - It is very critical that you read coding guidelines in Section E in README file. + It is very critical that you read coding guidelines in Section E in README file. """ from xnu import * import sys, shlex from utils import * from core.lazytarget import * +import time import xnudefines def GetProcNameForTask(task): @@ -184,16 +185,27 @@ def GetASTSummary(ast): 0x1000:'G', 0x2000:'T', 0x4000:'T', 0x8000:'T', 0x10000:'S'} state_str = '' mask = 0x1 - while mask <= 0x10000 : + while mask <= 0x10000: state_str += thread_state_chars[int(state & mask)] mask = mask << 1 - + return state_str +@lldb_type_summary(['kcdata_descriptor *', 'kcdata_descriptor_t']) +@header("{0: <20s} {1: <20s} {2: <20s} {3: <10s} {4: <5s}".format("kcdata_descriptor", "begin_addr", "cur_pos", "size", "flags")) +def GetKCDataSummary(kcdata): + """ Summarizes kcdata_descriptor structure + params: kcdata: value - value object representing kcdata_descriptor + returns: str - summary of the kcdata object + """ + format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <10d} {4: <#05x}" + return format_string.format(kcdata, kcdata.kcd_addr_begin, kcdata.kcd_addr_end, kcdata.kcd_length, kcdata.kcd_flags) + + @lldb_type_summary(['task', 'task_t']) @header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: <5s}".format("task","vm_map", "ipc_space", "#acts", "flags")) -def GetTaskSummary(task): +def GetTaskSummary(task, showcorpse=False): """ Summarizes the important fields in task structure. params: task: value - value object representing a task in kernel returns: str - summary of the task @@ -214,11 +226,20 @@ def GetTaskSummary(task): task_flags += 'D' if int(tib.iit_assertcnt) > 0: task_flags += 'B' + + # check if corpse flag is set + if unsigned(task.t_flags) & 0x20: + task_flags += 'C' + if unsigned(task.t_flags) & 0x40: + task_flags += 'P' + out_string += format_string.format(task, task.map, task.itk_space, thread_count, task_flags) + if showcorpse is True and unsigned(task.corpse_info) != 0: + out_string += " " + GetKCDataSummary(task.corpse_info) return out_string @lldb_type_summary(['thread *', 'thread_t']) -@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', 'wait_queue', 'wait_event', 'wmesg', 'thread_name')) +@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')) def GetThreadSummary(thread): """ Summarize the thread structure. It decodes the wait state and waitevents from the data in the struct. params: thread: value - value objecte representing a thread in kernel @@ -232,6 +253,7 @@ def GetThreadSummary(thread): H - Terminated A - Terminated and on termination queue I - Idle thread + C - Crashed thread policy flags: B - darwinbg @@ -248,7 +270,7 @@ def GetThreadSummary(thread): thread_id = hex(thread.thread_id) thread_name = '' processor = hex(thread.last_processor) - base_priority = str(int(thread.priority)) + base_priority = str(int(thread.base_pri)) sched_priority = str(int(thread.sched_pri)) sched_mode = '' mode = str(thread.sched_mode) @@ -302,7 +324,10 @@ def GetThreadSummary(thread): while mask <= 0x80 : state_str += thread_state_chars[int(state & mask)] mask = mask << 1 - + + if int(thread.inspection): + state_str += 'C' + ast = int(thread.ast) | int(thread.reason) ast_str = GetASTSummary(ast) @@ -312,7 +337,7 @@ def GetThreadSummary(thread): wait_message = '' if ( state & 0x1 ) != 0: #we need to look at the waitqueue as well - wait_queue_str = str("{0: <#020x}".format(int(hex(thread.wait_queue), 16))) + wait_queue_str = str("{0: <#020x}".format(int(hex(thread.waitq), 16))) wait_event_str = str("{0: <#020x}".format(int(hex(thread.wait_event), 16))) wait_event_str_sym = kern.Symbolicate(int(hex(thread.wait_event), 16)) if len(wait_event_str_sym) > 0: @@ -324,23 +349,212 @@ def GetThreadSummary(thread): 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) return out_string - -@lldb_type_summary(['coalition_t', 'coalition']) -@header("{:>18s} {:>10s} {:>8s} {:>8s} {:>8s} {:>8s}".format("coalition", "id", "refcount", "active", "focal", "nonfocal")) -def GetCoalitionSummary(coal): - out_string = "" - format_string = '{:>#018x} {:>10d} {:>8d} {:>8d} {:>8d} {:>8d}' - flags_string = '' +def GetTaskRoleString(role): + role_strs = { + 0 : "TASK_UNSPECIFIED", + 1 : "TASK_FOREGROUND_APPLICATION", + 2 : "TASK_BACKGROUND_APPLICATION", + 3 : "TASK_CONTROL_APPLICATION", + 4 : "TASK_GRAPHICS_SERVER", + 5 : "TASK_THROTTLE_APPLICATION", + 6 : "TASK_NONUI_APPLICATION", + 7 : "TASK_DEFAULT_APPLICATION", + } + return role_strs[int(role)] + +def GetCoalitionFlagString(coal): + flags = [] + if (coal.privileged): + flags.append('privileged') + if (coal.termrequested): + flags.append('termrequested') if (coal.terminated): - flags_string += ' terminated' + flags.append('terminated') if (coal.reaped): - flags_string += ' reaped' - out_string += format_string.format(coal, coal.id, coal.ref_count, coal.active_count, coal.focal_tasks_count, coal.non_focal_tasks_count) + flags.append('reaped') + if (coal.notified): + flags.append('notified') + return "|".join(flags) + +def GetCoalitionTasks(queue, coal_type, thread_details=False): + sfi_strs = { + 0x0 : "SFI_CLASS_UNSPECIFIED", + 0x1 : "SFI_CLASS_DARWIN_BG", + 0x2 : "SFI_CLASS_APP_NAP", + 0x3 : "SFI_CLASS_MANAGED_FOCAL", + 0x4 : "SFI_CLASS_MANAGED_NONFOCAL", + 0x5 : "SFI_CLASS_DEFAULT_FOCAL", + 0x6 : "SFI_CLASS_DEFAULT_NONFOCAL", + 0x7 : "SFI_CLASS_KERNEL", + 0x8 : "SFI_CLASS_OPTED_OUT", + 0x9 : "SFI_CLASS_UTILITY", + 0xA : "SFI_CLASS_LEGACY_FOCAL", + 0xB : "SFI_CLASS_LEGACY_NONFOCAL", + 0xC : "SFI_CLASS_USER_INITIATED_FOCAL", + 0xD : "SFI_CLASS_USER_INITIATED_NONFOCAL", + 0xE : "SFI_CLASS_USER_INTERACTIVE_FOCAL", + 0xF : "SFI_CLASS_USER_INTERACTIVE_NONFOCAL", + 0x10 : "SFI_CLASS_MAINTENANCE", + } + tasks = [] + field_name = 'task_coalition' + for task in IterateLinkageChain(queue, 'task *', field_name, coal_type * sizeof('queue_chain_t')): + task_str = "({0: ): + COALITION_TYPE_RESOURCE + COALITION_TYPE_JETSAM + """ + if type == 0: # COALITION_TYPE_RESOURCE + return 'RESOURCE' + if type == 1: + return 'JETSAM' + return '' + +def GetResourceCoalitionSummary(coal, verbose=False): + """ Summarize a resource coalition + """ + out_string = "Resource Coalition:\n\t Ledger:\n" + thread_details = False + if config['verbosity'] > vSCRIPT: + thread_details = True + ledgerp = coal.r.ledger + if verbose and unsigned(ledgerp) != 0: + i = 0 + while i != ledgerp.l_template.lt_cnt: + out_string += "\t\t" + out_string += GetLedgerEntrySummary(kern.globals.task_ledger_template, ledgerp.l_entries[i], i) + i = i + 1 + out_string += "\t bytesread {0: vSCRIPT: + thread_details = True + if unsigned(coal.j.leader) == 0: + out_string += "\n\t NO Leader!" + else: + out_string += "\n\t Leader:\n\t\t" + out_string += "({0: " + typestr = GetCoalitionTypeString(coal.type) + flagstr = GetCoalitionFlagString(coal) + out_string = "" + out_string += "Coalition {c: <#020x}\n\tID {c.id: + """ + verbose = False + if config['verbosity'] > vHUMAN: + verbose = True + if not cmd_args: + print "No arguments passed" + print ShowCoalitionInfo.__doc__ + return False + coal = kern.GetValueFromAddress(cmd_args[0], 'coalition *') + if not coal: + print "unknown arguments:", str(cmd_args) + return False + print GetCoalitionInfo(coal, verbose) + +# EndMacro: showcoalitioninfo + +# Macro: showallcoalitions + +@lldb_command('showallcoalitions') +def ShowAllCoalitions(cmd_args=None): + """ Print a summary listing of all the coalitions + """ + global kern + print GetCoalitionSummary.header + for c in kern.coalitions: + print GetCoalitionSummary(c) + +# EndMacro: showallcoalitions + +# Macro: showtaskcoalitions + +@lldb_command('showtaskcoalitions', 'F:') +def ShowTaskCoalitions(cmd_args=None, cmd_options={}): + """ + """ + task_list = [] + if "-F" in cmd_options: + task_list = FindTasksByName(cmd_options["-F"]) + elif cmd_args: + t = kern.GetValueFromAddress(cmd_args[0], 'task *') + task_list.append(t) + else: + raise ArgumentError("No arguments passed") + + if len(task_list) > 0: + print GetCoalitionSummary.header + for task in task_list: + print GetCoalitionSummary(task.coalition[0]) + print GetCoalitionSummary(task.coalition[1]) + +# EndMacro: showtaskcoalitions + @lldb_type_summary(['proc', 'proc *']) @header("{0: >6s} {1: ^20s} {2: >14s} {3: ^10s} {4: <20s}".format("pid", "process", "io_policy", "wq_state", "command")) def GetProcSummary(proc): @@ -439,17 +653,29 @@ def GetKQueueSummary(kq): state_str += ' ' + xnudefines.kq_state_strings[int(state & mask)] mask = mask << 1 out_string += format_string.format(o=kq, st_str=state_str) + out_string += "\n" + GetKnoteSummary.header + for kn in IterateTAILQ_HEAD(kq.kq_head, 'kn_tqe'): + out_string += "\n" + GetKnoteSummary(kn) return out_string @lldb_type_summary(['knote *']) -@header("{0: <20s}".format('knote')) +@header("{0: <20s} {1: <10s} {2: <10s} {3: <20s} {4: <20s} {5: <30s}".format('knote', 'ident', 'kev_flags', 'kn_kq', 'filtops', ' status')) def GetKnoteSummary(kn): """ Summarizes a knote and related information returns: str - summary of knote """ out_string = "" - format_string = "{o: <#020x}" - out_string += format_string.format(o=kn) + format_string = "{o: <#020x} {o.kn_kevent.ident: <#010X} {o.kn_kevent.flags: <#010X} {o.kn_kq: <#020X} {ops_str: <20s} {st_str: <30s}" + state = unsigned(kn.kn_status) + fops_str = kern.Symbolicate(unsigned(kn.kn_fop)) + mask = 0x1 + status_desc = '' + while mask <= 0x40: + if state & mask: + status_desc += ' ' + xnudefines.kn_state_strings[int(state & mask)] + mask = mask << 1 + + out_string += format_string.format(o=kn, st_str=status_desc, ops_str=fops_str) return out_string # Macro: showtask @@ -596,6 +822,51 @@ def ShowProcFiles(cmd_args=None): #EndMacro: showprocfiles + +def GetProcKqueues(proc): + filetype_KQUEUE = 5 + + proc_filedesc = proc.p_fd + proc_lastfile = unsigned(proc_filedesc.fd_lastfile) + proc_ofiles = proc_filedesc.fd_ofiles + + queues = list() + + if unsigned(proc_ofiles) == 0: + return queues + + count = 0 + + while count <= proc_lastfile: + if unsigned(proc_ofiles[count]) != 0: + proc_fd_flags = proc_ofiles[count].f_flags + proc_fd_fglob = proc_ofiles[count].f_fglob + proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type) + if proc_fd_ftype == filetype_KQUEUE: + q = Cast(proc_fd_fglob.fg_data, 'struct kqueue *') + queues.append(q) + count += 1 + + return queues + +def GetAllKqueues(): + for t in kern.tasks: + if unsigned(t.bsd_info) == 0: + continue + pval = Cast(t.bsd_info, 'proc *') + for kq in GetProcKqueues(pval): + yield kq + +#Macro: showallkqueues +@lldb_command('showallkqueues' ,'') +def ShowAllKqueues(cmd_args=[], cmd_options={}): + """ Display a summary of all the kqueues in the system """ + for kq in GetAllKqueues(): + print GetKQueueSummary.header + print GetKQueueSummary(kq) + print "\n\n" +#EndMacro: showallkqueues + #Macro: showkqueue @lldb_command('showkqueue' ,'') def ShowKQueue(cmd_args=[], cmd_options={}): @@ -699,6 +970,40 @@ def ShowAllTTYDevs(cmd_args=[], cmd_options={}): #EndMacro: showallttydevs +#Macro: dumpthread_terminate_queue + +@lldb_command('dumpthread_terminate_queue') +def DumpThreadTerminateQueue(cmd_args=None): + """ Displays the contents of the specified call_entry queue. + Usage: dumpthread_terminate_queue + """ + + count = 0 + print GetThreadSummary.header + for th in IterateQueue(addressof(kern.globals.thread_terminate_queue), 'struct thread *', 'q_link'): + print GetThreadSummary(th) + count += 1 + print "{0: """ if not cmd_args: - print DumpCallQueue.__doc__ - return + raise ArgumentError("Invalid arguments") + print "{0: <18s} {1: <18s} {2: <18s} {3: <64s} {4: <18s}".format('CALL_ENTRY', 'PARAM0', 'PARAM1', 'DEADLINE', 'FUNC') callhead = kern.GetValueFromAddress(cmd_args[0], 'queue_head_t *') count = 0 @@ -721,55 +1026,23 @@ def DumpCallQueue(cmd_args=None): #EndMacro: dumpcallqueue -@lldb_command('showallcoalitions') -def ShowAllCoalitions(cmd_args=None): - """ Routine to print a summary listing of all the coalitions +@lldb_command('showalltasklogicalwrites') +def ShowAllTaskIOStats(cmd_args=None): + """ Commad to print I/O stats for all tasks """ - global kern - - role_strs = { - 0 : "TASK_UNSPECIFIED", - 1 : "TASK_FOREGROUND_APPLICATION", - 2 : "TASK_BACKGROUND_APPLICATION", - 3 : "TASK_CONTROL_APPLICATION", - 4 : "TASK_GRAPHICS_SERVER", - 5 : "TASK_THROTTLE_APPLICATION", - 6 : "TASK_NONUI_APPLICATION", - 7 : "TASK_DEFAULT_APPLICATION", - } - - sfi_strs = { - 0x0 : "SFI_CLASS_UNSPECIFIED", - 0x1 : "SFI_CLASS_DARWIN_BG", - 0x2 : "SFI_CLASS_APP_NAP", - 0x3 : "SFI_CLASS_MANAGED_FOCAL", - 0x4 : "SFI_CLASS_MANAGED_NONFOCAL", - 0x5 : "SFI_CLASS_DEFAULT_FOCAL", - 0x6 : "SFI_CLASS_DEFAULT_NONFOCAL", - 0x7 : "SFI_CLASS_KERNEL", - 0x8 : "SFI_CLASS_OPTED_OUT", - 0x9 : "SFI_CLASS_UTILITY", - 0xA : "SFI_CLASS_LEGACY_FOCAL", - 0xB : "SFI_CLASS_LEGACY_NONFOCAL", - 0xC : "SFI_CLASS_USER_INITIATED_FOCAL", - 0xD : "SFI_CLASS_USER_INITIATED_NONFOCAL", - 0xE : "SFI_CLASS_USER_INTERACTIVE_FOCAL", - 0xF : "SFI_CLASS_USER_INTERACTIVE_NONFOCAL", - 0x10 : "SFI_CLASS_MAINTENANCE", - } - - - print GetCoalitionSummary.header - for c in kern.coalitions: - print GetCoalitionSummary(c) - for task in IterateQueue(c.tasks, "task_t", "coalition_tasks"): - print "\t" + hex(task) + " " + GetProcNameForTask(task) + " " + role_strs[int(task.effective_policy.t_role)] - for thread in IterateQueue(task.threads, "thread_t", "task_threads"): - print "\t\t" + hex(thread) + " " + sfi_strs[int(thread.sfi_class)] + print "{0: <20s} {1: <20s} {2: <20s} {3: <20s} {4: <20s} {5: <20s}".format("task", "Immediate Writes", "Deferred Writes", "Invalidated Writes", "Metadata Writes", "name") + for t in kern.tasks: + pval = Cast(t.bsd_info, 'proc *') + print "{0: <#18x} {1: >20d} {2: >20d} {3: >20d} {4: >20d} {5: <20s}".format(t, + t.task_immediate_writes, + t.task_deferred_writes, + t.task_invalidated_writes, + t.task_metadata_writes, + str(pval.p_comm)) -@lldb_command('showalltasks') -def ShowAllTasks(cmd_args=None): +@lldb_command('showalltasks','C') +def ShowAllTasks(cmd_args=None, cmd_options={}): """ Routine to print a summary listing of all the tasks wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung @@ -777,12 +1050,20 @@ def ShowAllTasks(cmd_args=None): NORM - normal I/O explicitly requested (this is the default) PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) + Usage: (lldb) showalltasks -C : describe the corpse structure """ global kern - print GetTaskSummary.header + " " + GetProcSummary.header + extra_hdr = '' + showcorpse = False + if '-C' in cmd_options: + showcorpse = True + extra_hdr += " " + GetKCDataSummary.header + + print GetTaskSummary.header + extra_hdr + " " + GetProcSummary.header for t in kern.tasks: pval = Cast(t.bsd_info, 'proc *') - print GetTaskSummary(t) +" "+ GetProcSummary(pval) + out_str = GetTaskSummary(t, showcorpse) + " " + GetProcSummary(pval) + print out_str ZombTasks() @lldb_command('showterminatedtasks') @@ -951,6 +1232,38 @@ def SwitchToAct(cmd_args=None): if not LazyTarget.GetProcess().SetSelectedThread(lldbthread): print "Failed to switch thread." return + +@lldb_command('switchtoregs') +def SwitchToRegs(cmd_args=None): + """ Routine to switch to a register state. + Usage: (lldb) switchtoregs + This command creates a fake thread in lldb with the saved register state. + Note: This command ONLY works for ARM based kernel setup. + """ + + if cmd_args == None or len(cmd_args) < 1: + raise ArgumentError("No arguments passed") + + lldb_process = LazyTarget.GetProcess() + + saved_state = ArgumentStringToInt(cmd_args[0]) + # any change to this logic requires change in operating_system.py as well + fake_thread_id = 0xdead0000 | (saved_state & ~0xffff0000) + fake_thread_id = fake_thread_id & 0xdeadffff + lldb_process.CreateOSPluginThread(0xdeadbeef, saved_state) + lldbthread = lldb_process.GetThreadByID(int(fake_thread_id)) + + if not lldbthread.IsValid(): + print "Failed to create thread" + return + + lldb_process.selected_thread = lldbthread + if not lldb_process.SetSelectedThread(lldbthread): + print "Failed to switch thread" + print "Switched to Fake thread created from register state at 0x%x" % saved_state + + + # Macro: showallstacks @lldb_command('showallstacks') def ShowAllStacks(cmd_args=None): @@ -1018,7 +1331,7 @@ def GetFullBackTrace(frame_addr, verbosity = vHUMAN, prefix = ""): # lldb unable to find symbol for _mh_execute_header mh_execute_addr = int(lldb_run_command('p/x (uintptr_t *)&_mh_execute_header').split('=')[-1].strip(), 16) while frame_ptr and frame_ptr != previous_frame_ptr and bt_count < 128: - if (kern.arch != 'arm' and frame_ptr < mh_execute_addr) or (kern.arch == 'arm' and frame_ptr > mh_execute_addr): + if (kern.arch not in ('arm', 'arm64') and frame_ptr < mh_execute_addr) or (kern.arch in ('arm', 'arm64') and frame_ptr > mh_execute_addr): break pc_val = kern.GetValueFromAddress(frame_ptr + kern.ptrsize,'uintptr_t *') pc_val = unsigned(dereference(pc_val)) @@ -1173,7 +1486,30 @@ def GetProcessorSummary(processor): if processor_state in processor_states: processor_state_str = "{0: <11s} ".format(processor_states[processor_state]) - out_str = "Processor {: <#018x} cpu_id {:>#4x} State {: