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""" 5from xnu import * 6import sys, shlex 7from utils import * 8import xnudefines 9from process import * 10 11# Macro: memstats 12@lldb_command('memstats') 13def Memstats(cmd_args=None): 14 """ Prints out a summary of various memory statistics. In particular vm_page_wire_count should be greater than 2K or you are under memory pressure. 15 """ 16 try: 17 print "memorystatus_level: {: >10d}".format(kern.globals.memorystatus_level) 18 except ValueError: 19 pass 20 try: 21 print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages) 22 except ValueError: 23 pass 24 print "vm_page_throttled_count: {: >10d}".format(kern.globals.vm_page_throttled_count) 25 print "vm_page_active_count: {: >10d}".format(kern.globals.vm_page_active_count) 26 print "vm_page_inactive_count: {: >10d}".format(kern.globals.vm_page_inactive_count) 27 print "vm_page_wire_count: {: >10d}".format(kern.globals.vm_page_wire_count) 28 print "vm_page_free_count: {: >10d}".format(kern.globals.vm_page_free_count) 29 print "vm_page_purgeable_count: {: >10d}".format(kern.globals.vm_page_purgeable_count) 30 print "vm_page_inactive_target: {: >10d}".format(kern.globals.vm_page_inactive_target) 31 print "vm_page_free_target: {: >10d}".format(kern.globals.vm_page_free_target) 32 print "inuse_ptepages_count: {: >10d}".format(kern.globals.inuse_ptepages_count) 33 print "vm_page_free_reserved: {: >10d}".format(kern.globals.vm_page_free_reserved) 34 35@xnudebug_test('test_memstats') 36def TestMemstats(kernel_target, config, lldb_obj, isConnected ): 37 """ Test the functionality of memstats command 38 returns 39 - False on failure 40 - True on success 41 """ 42 if not isConnected: 43 print "Target is not connected. Cannot test memstats" 44 return False 45 res = lldb.SBCommandReturnObject() 46 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res) 47 result = res.GetOutput() 48 if result.split(":")[1].strip().find('None') == -1 : 49 return True 50 else: 51 return False 52 53# EndMacro: memstats 54 55# Macro: showmemorystatus 56def CalculateLedgerPeak(phys_footprint_entry): 57 """ Internal function to calculate ledger peak value for the given phys footprint entry 58 params: phys_footprint_entry - value representing struct ledger_entry * 59 return: value - representing the ledger peak for the given phys footprint entry 60 """ 61 now = kern.globals.sched_tick / 20 62 ledger_peak = phys_footprint_entry.le_credit - phys_footprint_entry.le_debit 63 if (now - phys_footprint_entry._le.le_peaks[0].le_time <= 1) and (phys_footprint_entry._le.le_peaks[0].le_max > ledger_peak): 64 ledger_peak = phys_footprint_entry._le.le_peaks[0].le_max 65 if (now - phys_footprint_entry._le.le_peaks[1].le_time <= 1) and (phys_footprint_entry._le.le_peaks[1].le_max > ledger_peak): 66 ledger_peak = phys_footprint_entry._le.le_peaks[1].le_max 67 return ledger_peak 68 69@header("{: >8s} {: >22s} {: >22s} {: >11s} {: >11s} {: >12s} {: >10s} {: >13s} {: ^10s} {: >8s} {: <20s}\n".format( 70'pid', 'effective priority', 'requested priority', 'state', 'user_data', 'physical', 'iokit', 'footprint', 71'spike', 'limit', 'command')) 72def GetMemoryStatusNode(proc_val): 73 """ Internal function to get memorystatus information from the given proc 74 params: proc - value representing struct proc * 75 return: str - formatted output information for proc object 76 """ 77 out_str = '' 78 task_val = Cast(proc_val.task, 'task *') 79 task_ledgerp = task_val.ledger 80 81 task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem] 82 task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mem] 83 task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint] 84 page_size = kern.globals.page_size 85 86 phys_mem_footprint = (task_physmem_footprint_ledger_entry.le_credit - task_physmem_footprint_ledger_entry.le_debit) / page_size 87 iokit_footprint = (task_iokit_footprint_ledger_entry.le_credit - task_iokit_footprint_ledger_entry.le_debit) / page_size 88 phys_footprint = (task_phys_footprint_ledger_entry.le_credit - task_phys_footprint_ledger_entry.le_debit) / page_size 89 phys_footprint_limit = task_phys_footprint_ledger_entry.le_limit / page_size 90 ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry) 91 phys_footprint_spike = ledger_peak / page_size 92 93 format_string = '{0: >8d} {1: >22d} {2: >22d} {3: #011x} {4: #011x} {5: >12d} {6: >10d} {7: >13d}' 94 out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority, 95 proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_userdata, 96 phys_mem_footprint, iokit_footprint, phys_footprint) 97 if phys_footprint != phys_footprint_spike: 98 out_str += "{: ^12d}".format(phys_footprint_spike) 99 else: 100 out_str += "{: ^12s}".format('-') 101 out_str += "{: 8d} {: <20s}\n".format(phys_footprint_limit, proc_val.p_comm) 102 return out_str 103 104@lldb_command('showmemorystatus') 105def ShowMemoryStatus(cmd_args=None): 106 """ Routine to display each entry in jetsam list with a summary of pressure statistics 107 Usage: showmemorystatus 108 """ 109 bucket_index = 0 110 bucket_count = 20 111 print GetMemoryStatusNode.header 112 print "{: >91s} {: >10s} {: >13s} {: ^10s} {: >8s}\n".format("(pages)", "(pages)", "(pages)", 113 "(pages)", "(pages)") 114 while bucket_index < bucket_count: 115 current_bucket = kern.globals.memstat_bucket[bucket_index] 116 current_list = current_bucket.list 117 current_proc = Cast(current_list.tqh_first, 'proc *') 118 while unsigned(current_proc) != 0: 119 print GetMemoryStatusNode(current_proc) 120 current_proc = current_proc.p_memstat_list.tqe_next 121 bucket_index += 1 122 print "\n\n" 123 Memstats() 124 125# EndMacro: showmemorystatus 126 127# Macro: zprint 128 129@lldb_type_summary(['zone','zone_t']) 130@header("{:^18s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^14s} {:<20s}".format( 131'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'SLK', 'FLAGS', 'NAME')) 132def GetZoneSummary(zone): 133 """ Summarize a zone with important information. See help zprint for description of each field 134 params: 135 zone: value - obj representing a zone in kernel 136 returns: 137 str - summary of the zone 138 """ 139 out_string = "" 140 format_string = '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d} {markings} {name:s} ' 141 pagesize = 4096 142 143 free_elements = (zone.cur_size / zone.elem_size) - zone.count 144 free_size = free_elements * zone.elem_size 145 146 alloc_count = zone.alloc_size / zone.elem_size 147 alloc_pages = zone.alloc_size / pagesize 148 alloc_slack = zone.alloc_size % zone.elem_size 149 marks = [ 150 ["collectable", "C"], 151 ["expandable", "X"], 152 ["noencrypt", "$"], 153 ["caller_acct", "@"], 154 ["exhaustible", "H"], 155 ["allows_foreign", "F"], 156 ["async_prio_refill", "R"], 157 ["no_callout", "O"], 158 ["zleak_on", "L"], 159 ["doing_alloc", "A"], 160 ["waiting", "W"], 161 ["doing_gc", "G"] 162 ] 163 if kern.arch == 'x86_64': 164 marks.append(["gzalloc_exempt", "M"]) 165 marks.append(["alignment_required", "N"]) 166 167 markings="" 168 for mark in marks: 169 if zone.__getattr__(mark[0]) : 170 markings+=mark[1] 171 else: 172 markings+=" " 173 out_string += format_string.format(zone, zone.cur_size, zone.page_count, 174 zone.count, free_elements, free_size, 175 zone.elem_size, zone.alloc_size, alloc_count, 176 alloc_pages, alloc_slack, name = zone.zone_name, markings=markings) 177 178 if zone.exhaustible : 179 out_string += "(max: {:d})".format(zone.max_size) 180 181 return out_string 182 183@lldb_command('zprint') 184def Zprint(cmd_args=None): 185 """ Routine to print a summary listing of all the kernel zones 186 All columns are printed in decimal 187 Legend: 188 C - collectable 189 X - expandable 190 $ - not encrypted during hibernation 191 @ - allocs and frees are accounted to caller process for KPRVT 192 H - exhaustible 193 F - allows foreign memory (memory not allocated from zone_map) 194 M - gzalloc will avoid monitoring this zone 195 R - will be refilled when below low water mark 196 O - does not allow refill callout to fill zone on noblock allocation 197 N - zone requires alignment (avoids padding this zone for debugging) 198 A - currently trying to allocate more backing memory from kernel_memory_allocate 199 W - another thread is waiting for more memory 200 L - zone is being monitored by zleaks 201 G - currently running GC 202 """ 203 global kern 204 print GetZoneSummary.header 205 for zval in kern.zones: 206 print GetZoneSummary(zval) 207 208@xnudebug_test('test_zprint') 209def TestZprint(kernel_target, config, lldb_obj, isConnected ): 210 """ Test the functionality of zprint command 211 returns 212 - False on failure 213 - True on success 214 """ 215 if not isConnected: 216 print "Target is not connected. Cannot test memstats" 217 return False 218 res = lldb.SBCommandReturnObject() 219 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res) 220 result = res.GetOutput() 221 if len(result.split("\n")) > 2: 222 return True 223 else: 224 return False 225 226 227# EndMacro: zprint 228 229# Macro: showzfreelist 230 231def ShowZfreeListHeader(zone): 232 """ Helper routine to print a header for zone freelist. 233 (Since the freelist does not have a custom type, this is not defined as a Type Summary). 234 params: 235 zone:zone_t - Zone object to print header info 236 returns: 237 None 238 """ 239 out_str = "" 240 out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR') 241 out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format( 242 zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, kern.globals.zp_factor) 243 out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format( 244 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)') 245 print out_str 246 247def ShowZfreeListChain(zone, zfirst, zlimit): 248 """ Helper routine to print a zone free list chain 249 params: 250 zone: zone_t - Zone object 251 zfirst: void * - A pointer to the first element of the free list chain 252 zlimit: int - Limit for the number of elements to be printed by showzfreelist 253 returns: 254 None 255 """ 256 current = Cast(zfirst, 'void *') 257 while ShowZfreeList.elts_found < zlimit: 258 ShowZfreeList.elts_found += 1 259 znext = dereference(Cast(current, 'vm_offset_t *')) 260 backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *') 261 backup_val = dereference(backup_ptr) 262 n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie)) 263 p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie)) 264 poison_str = '' 265 if p_unobfuscated == unsigned(znext): 266 poison_str = "P ({0: <d})".format(ShowZfreeList.elts_found - ShowZfreeList.last_poisoned) 267 ShowZfreeList.last_poisoned = ShowZfreeList.elts_found 268 else: 269 if n_unobfuscated != unsigned(znext): 270 poison_str = "INVALID" 271 print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format( 272 ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str) 273 if unsigned(znext) == 0: 274 break 275 current = Cast(znext, 'void *') 276 277@static_var('elts_found',0) 278@static_var('last_poisoned',0) 279@lldb_command('showzfreelist') 280def ShowZfreeList(cmd_args=None): 281 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element. 282 Usage: showzfreelist <zone> [iterations] 283 284 Will walk up to 50 elements by default, pass a limit in 'iterations' to override. 285 """ 286 if not cmd_args: 287 print ShowZfreeList.__doc__ 288 return 289 ShowZfreeList.elts_found = 0 290 ShowZfreeList.last_poisoned = 0 291 292 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *') 293 zlimit = 50 294 if len(cmd_args) >= 2: 295 zlimit = ArgumentStringToInt(cmd_args[1]) 296 ShowZfreeListHeader(zone) 297 298 if unsigned(zone.use_page_list) == 1: 299 if unsigned(zone.allows_foreign) == 1: 300 for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'): 301 if ShowZfreeList.elts_found == zlimit: 302 break 303 zfirst = Cast(free_page_meta.elements, 'void *') 304 if unsigned(zfirst) != 0: 305 ShowZfreeListChain(zone, zfirst, zlimit) 306 for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'): 307 if ShowZfreeList.elts_found == zlimit: 308 break 309 zfirst = Cast(free_page_meta.elements, 'void *') 310 if unsigned(zfirst) != 0: 311 ShowZfreeListChain(zone, zfirst, zlimit) 312 for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'): 313 if ShowZfreeList.elts_found == zlimit: 314 break 315 zfirst = Cast(free_page_meta.elements, 'void *') 316 if unsigned(zfirst) != 0: 317 ShowZfreeListChain(zone, zfirst, zlimit) 318 else: 319 zfirst = Cast(zone.free_elements, 'void *') 320 if unsigned(zfirst) != 0: 321 ShowZfreeListChain(zone, zfirst, zlimit) 322 323 if ShowZfreeList.elts_found == zlimit: 324 print "Stopped at {0: <d} elements!".format(zlimit) 325 else: 326 print "Found {0: <d} elements!".format(ShowZfreeList.elts_found) 327 328# EndMacro: showzfreelist 329 330# Macro: zstack 331 332@lldb_command('zstack') 333def Zstack(cmd_args=None): 334 """ Zone leak debugging: Print the stack trace of log element at <index>. If a <count> is supplied, it prints <count> log elements starting at <index>. 335 Usage: zstack <index> [<count>] 336 337 The suggested usage is to look at indexes below zcurrent and look for common stack traces. 338 The stack trace that occurs the most is probably the cause of the leak. Find the pc of the 339 function calling into zalloc and use the countpcs command to find out how often that pc occurs in the log. 340 The pc occuring in a high percentage of records is most likely the source of the leak. 341 342 The findoldest command is also useful for leak debugging since it identifies the oldest record 343 in the log, which may indicate the leaker. 344 """ 345 if not cmd_args: 346 print Zstack.__doc__ 347 return 348 if int(kern.globals.log_records) == 0: 349 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args." 350 return 351 if int(kern.globals.zlog_btlog) == 0: 352 print "Zone logging enabled, but zone has not been initialized yet." 353 return 354 355 count = 1 356 if len(cmd_args) >= 2: 357 count = ArgumentStringToInt(cmd_args[1]) 358 zstack_index = unsigned(cmd_args[0]) 359 while count and (zstack_index != 0xffffff): 360 zstack_record_offset = zstack_index * unsigned(kern.globals.zlog_btlog.btrecord_size) 361 zstack_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + zstack_record_offset, 'btlog_record_t *') 362 ShowZStackRecord(zstack_record, zstack_index) 363 zstack_index = zstack_record.next 364 count -= 1 365 366# EndMacro : zstack 367 368# Macro: findoldest 369 370@lldb_command('findoldest') 371def FindOldest(cmd_args=None): 372 """ Zone leak debugging: find and print the oldest record in the log. 373 374 Once it prints a stack trace, find the pc of the caller above all the zalloc, kalloc and 375 IOKit layers. Then use the countpcs command to see how often this caller has allocated 376 memory. A caller with a high percentage of records in the log is probably the leaker. 377 """ 378 if int(kern.globals.log_records) == 0: 379 print FindOldest.__doc__ 380 return 381 if int(kern.globals.zlog_btlog) == 0: 382 print "Zone logging enabled, but zone has not been initialized yet." 383 return 384 index = kern.globals.zlog_btlog.head 385 if unsigned(index) != 0xffffff: 386 print "Oldest record is at log index: {0: <d}".format(index) 387 Zstack([index]) 388 else: 389 print "No Records Present" 390 391# EndMacro : findoldest 392 393# Macro: countpcs 394 395@lldb_command('countpcs') 396def Countpcs(cmd_args=None): 397 """ Zone leak debugging: search the log and print a count of all log entries that contain the given <pc> 398 in the stack trace. 399 Usage: countpcs <pc> 400 401 This is useful for verifying a suspected <pc> as being the source of 402 the leak. If a high percentage of the log entries contain the given <pc>, then it's most 403 likely the source of the leak. Note that this command can take several minutes to run. 404 """ 405 if not cmd_args: 406 print Countpcs.__doc__ 407 return 408 if int(kern.globals.log_records) == 0: 409 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args." 410 return 411 if int(kern.globals.zlog_btlog) == 0: 412 print "Zone logging enabled, but zone has not been initialized yet." 413 return 414 415 cpcs_index = unsigned(kern.globals.zlog_btlog.head) 416 target_pc = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *')) 417 found = 0 418 depth = unsigned(kern.globals.zlog_btlog.btrecord_btdepth) 419 420 while cpcs_index != 0xffffff: 421 cpcs_record_offset = cpcs_index * unsigned(kern.globals.zlog_btlog.btrecord_size) 422 cpcs_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + cpcs_record_offset, 'btlog_record_t *') 423 frame = 0 424 while frame < depth: 425 frame_pc = unsigned(cpcs_record.bt[frame]) 426 if frame_pc == target_pc: 427 found += 1 428 break 429 frame += 1 430 cpcs_index = cpcs_record.next 431 print "Occured {0: <d} times in log ({1: <d}{2: <s} of records)".format(found, (found * 100)/unsigned(kern.globals.zlog_btlog.activecount), '%') 432 433# EndMacro: countpcs 434 435# Macro: findelem 436 437@lldb_command('findelem') 438def FindElem(cmd_args=None): 439 """ Zone corruption debugging: search the log and print out the stack traces for all log entries that 440 refer to the given zone element. 441 Usage: findelem <elem addr> 442 443 When the kernel panics due to a corrupted zone element, get the 444 element address and use this command. This will show you the stack traces of all logged zalloc and 445 zfree operations which tells you who touched the element in the recent past. This also makes 446 double-frees readily apparent. 447 """ 448 if not cmd_args: 449 print FindElem.__doc__ 450 return 451 if int(kern.globals.log_records) == 0: 452 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args." 453 return 454 if int(kern.globals.zlog_btlog) == 0: 455 print "Zone logging enabled, but zone has not been initialized yet." 456 return 457 458 target_element = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *')) 459 index = unsigned(kern.globals.zlog_btlog.head) 460 prev_op = -1 461 462 while index != 0xffffff: 463 findelem_record_offset = index * unsigned(kern.globals.zlog_btlog.btrecord_size) 464 findelem_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + findelem_record_offset, 'btlog_record_t *') 465 if unsigned(findelem_record.element) == target_element: 466 Zstack([index]) 467 if int(findelem_record.operation) == prev_op: 468 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8)) 469 prev_op = int(findelem_record.operation) 470 index = findelem_record.next 471 472# EndMacro: findelem 473 474# Macro: btlog_find 475 476@lldb_command('btlog_find', "A") 477def BtlogFind(cmd_args=None, cmd_options={}): 478 """ Search the btlog_t for entries corresponding to the given element. 479 Use -A flag to print all entries. 480 Usage: btlog_find <btlog_t> <element> 481 Usage: btlog_find <btlog_t> -A 482 Note: Backtraces will be in chronological order, with oldest entries aged out in FIFO order as needed. 483 """ 484 if not cmd_args: 485 raise ArgumentError("Need a btlog_t parameter") 486 btlog = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 487 printall = False 488 target_elem = 0xffffff 489 490 if "-A" in cmd_options: 491 printall = True 492 else: 493 if not printall and len(cmd_args) < 2: 494 raise ArgumentError("<element> is missing in args. Need a search pointer.") 495 target_elem = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *')) 496 497 index = unsigned(btlog.head) 498 progress = 0 499 record_size = unsigned(btlog.btrecord_size) 500 while index != 0xffffff: 501 record_offset = index * record_size 502 record = kern.GetValueFromAddress(unsigned(btlog.btrecords) + record_offset, 'btlog_record_t *') 503 if unsigned(record.element) == target_elem or printall: 504 print '{0: <s} OP {1: <d} {2: <#0x} {3: <s}\n'.format(('-' * 8), record.operation, target_elem, ('-' * 8)) 505 ShowBtlogBacktrace(btlog.btrecord_btdepth, record) 506 index = record.next 507 progress += 1 508 if (progress % 1000) == 0: print '{0: <d} entries searched!\n'.format(progress) 509 510#EndMacro: btlog_find 511 512#Macro: showzalloc 513 514@lldb_command('showzalloc') 515def ShowZalloc(cmd_args=None): 516 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace. 517 Usage: showzalloc <index> 518 """ 519 if not cmd_args: 520 print ShowZalloc.__doc__ 521 return 522 if unsigned(kern.globals.zallocations) == 0: 523 print "zallocations array not initialized!" 524 return 525 zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])] 526 print zallocation 527 ShowZTrace([str(int(zallocation.za_trace_index))]) 528 529#EndMacro: showzalloc 530 531#Macro: showztrace 532 533@lldb_command('showztrace') 534def ShowZTrace(cmd_args=None): 535 """ Prints the backtrace from the ztraces array at index 536 Usage: showztrace <trace index> 537 """ 538 if not cmd_args: 539 print ShowZTrace.__doc__ 540 return 541 if unsigned(kern.globals.ztraces) == 0: 542 print "ztraces array not initialized!" 543 return 544 ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])] 545 print ztrace_addr 546 ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth) 547 548#EndMacro: showztrace 549 550#Macro: showztraceaddr 551 552@lldb_command('showztraceaddr') 553def ShowZTraceAddr(cmd_args=None): 554 """ Prints the struct ztrace passed in. 555 Usage: showztraceaddr <trace address> 556 """ 557 if not cmd_args: 558 print ShowZTraceAddr.__doc__ 559 return 560 ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *') 561 print dereference(ztrace_ptr) 562 ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth) 563 564#EndMacro: showztraceaddr 565 566#Macro: showzstacktrace 567 568@lldb_command('showzstacktrace') 569def ShowZstackTrace(cmd_args=None): 570 """ Routine to print a stacktrace stored by OSBacktrace. 571 Usage: showzstacktrace <saved stacktrace> [size] 572 573 size is optional, defaults to 15. 574 """ 575 if not cmd_args: 576 print ShowZstackTrace.__doc__ 577 return 578 void_ptr_type = gettype('void *') 579 void_double_ptr_type = void_ptr_type.GetPointerType() 580 trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type) 581 trace_size = 15 582 if len(cmd_args) >= 2: 583 trace_size = ArgumentStringToInt(cmd_args[1]) 584 ShowZstackTraceHelper(trace, trace_size) 585 586#EndMacro: showzstacktrace 587 588def ShowZstackTraceHelper(stack, depth): 589 """ Helper routine for printing a zstack. 590 params: 591 stack: void *[] - An array of pointers representing the Zstack 592 depth: int - The depth of the ztrace stack 593 returns: 594 None 595 """ 596 trace_current = 0 597 while trace_current < depth: 598 trace_addr = stack[trace_current] 599 symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr)) 600 if symbol_arr: 601 symbol_str = str(symbol_arr[0].addr) 602 else: 603 symbol_str = '' 604 print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str) 605 trace_current += 1 606 607#Macro: showtopztrace 608 609@lldb_command('showtopztrace') 610def ShowTopZtrace(cmd_args=None): 611 """ Shows the ztrace with the biggest size. 612 (According to top_ztrace, not by iterating through the hash table) 613 """ 614 top_trace = kern.globals.top_ztrace 615 print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace')) 616 print dereference(top_trace) 617 ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth) 618 619#EndMacro: showtopztrace 620 621#Macro: showzallocs 622 623@lldb_command('showzallocs') 624def ShowZallocs(cmd_args=None): 625 """ Prints all allocations in the zallocations table 626 """ 627 if unsigned(kern.globals.zallocations) == 0: 628 print "zallocations array not initialized!" 629 return 630 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE') 631 current_index = 0 632 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets) 633 allocation_count = 0 634 while current_index < max_zallocation: 635 current_zalloc = kern.globals.zallocations[current_index] 636 if int(current_zalloc.za_element) != 0: 637 print '{0: <5d} {1: <#018x} {2: <5d} {3: <15d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_trace_index, unsigned(current_zalloc.za_size)) 638 allocation_count += 1 639 current_index += 1 640 print 'Total Allocations: {0: <d}'.format(allocation_count) 641 642#EndMacro: showzallocs 643 644#Macro: showzallocsfortrace 645 646@lldb_command('showzallocsfortrace') 647def ShowZallocsForTrace(cmd_args=None): 648 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table 649 Usage: showzallocsfortrace <trace index> 650 """ 651 if not cmd_args: 652 print ShowZallocsForTrace.__doc__ 653 return 654 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE') 655 target_index = ArgumentStringToInt(cmd_args[0]) 656 current_index = 0 657 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets) 658 allocation_count = 0 659 while current_index < max_zallocation: 660 current_zalloc = kern.globals.zallocations[current_index] 661 if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)): 662 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size) 663 allocation_count += 1 664 current_index += 1 665 print 'Total Allocations: {0: <d}'.format(allocation_count) 666 667#EndMacro: showzallocsfortrace 668 669#Macro: showztraces 670 671@lldb_command('showztraces') 672def ShowZTraces(cmd_args=None): 673 """ Prints all traces with size > 0 674 """ 675 ShowZTracesAbove([0]) 676 677#EndMacro: showztraces 678 679#Macro: showztracesabove 680 681@lldb_command('showztracesabove') 682def ShowZTracesAbove(cmd_args=None): 683 """ Prints all traces with size greater than X 684 Usage: showztracesabove <size> 685 """ 686 if not cmd_args: 687 print ShowZTracesAbove.__doc__ 688 return 689 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE') 690 current_index = 0 691 ztrace_count = 0 692 max_ztrace = unsigned(kern.globals.zleak_trace_buckets) 693 while current_index < max_ztrace: 694 ztrace_current = kern.globals.ztraces[current_index] 695 if ztrace_current.zt_size > unsigned(cmd_args[0]): 696 print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size)) 697 ztrace_count += 1 698 current_index += 1 699 print 'Total traces: {0: <d}'.format(ztrace_count) 700 701#EndMacro: showztracesabove 702 703#Macro: showztracehistogram 704 705@lldb_command('showztracehistogram') 706def ShowZtraceHistogram(cmd_args=None): 707 """ Prints the histogram of the ztrace table 708 """ 709 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS') 710 current_index = 0 711 ztrace_count = 0 712 max_ztrace = unsigned(kern.globals.zleak_trace_buckets) 713 while current_index < max_ztrace: 714 ztrace_current = kern.globals.ztraces[current_index] 715 if ztrace_current.zt_hit_count != 0: 716 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions) 717 ztrace_count += 1 718 current_index += 1 719 print 'Total traces: {0: <d}'.format(ztrace_count) 720 721#EndMacro: showztracehistogram 722 723#Macro: showzallochistogram 724 725@lldb_command('showzallochistogram') 726def ShowZallocHistogram(cmd_args=None): 727 """ Prints the histogram for the zalloc table 728 """ 729 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT') 730 current_index = 0 731 zallocation_count = 0 732 max_ztrace = unsigned(kern.globals.zleak_alloc_buckets) 733 while current_index < max_ztrace: 734 zallocation_current = kern.globals.zallocations[current_index] 735 if zallocation_current.za_hit_count != 0: 736 print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count) 737 zallocation_count += 1 738 current_index += 1 739 print 'Total Allocations: {0: <d}'.format(zallocation_count) 740 741#EndMacro: showzallochistogram 742 743#Macro: showzstats 744 745@lldb_command('showzstats') 746def ShowZstats(cmd_args=None): 747 """ Prints the zone leak detection stats 748 """ 749 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions)) 750 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites)) 751 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded)) 752 753#EndMacro: showzstats 754 755def ShowBtlogBacktrace(depth, zstack_record): 756 """ Helper routine for printing a BT Log record backtrace stack. 757 params: 758 depth:int - The depth of the zstack record 759 zstack_record:btlog_record_t * - A BTLog record 760 returns: 761 None 762 """ 763 out_str = '' 764 frame = 0 765 if not zstack_record: 766 print "Zstack record none!" 767 return 768 depth_val = unsigned(depth) 769 while frame < depth_val: 770 frame_pc = zstack_record.bt[frame] 771 if not frame_pc or int(frame_pc) == 0: 772 break 773 symbol_arr = kern.SymbolicateFromAddress(frame_pc) 774 if symbol_arr: 775 symbol_str = str(symbol_arr[0].addr) 776 else: 777 symbol_str = '' 778 out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str) 779 frame += 1 780 print out_str 781 782def ShowZStackRecord(zstack_record, zstack_index): 783 """ Helper routine for printing a single zstack record 784 params: 785 zstack_record:btlog_record_t * - A BTLog record 786 zstack_index:int - Index for the record in the BTLog table 787 returns: 788 None 789 """ 790 out_str = ('-' * 8) 791 if zstack_record.operation == 1: 792 out_str += "ALLOC " 793 else: 794 out_str += "FREE " 795 out_str += "{0: <#0x} : Index {1: <d} {2: <s}\n".format(zstack_record.element, zstack_index, ('-' * 8)) 796 print out_str 797 ShowBtlogBacktrace(kern.globals.zlog_btlog.btrecord_btdepth, zstack_record) 798 799# Macro: showioalloc 800 801@lldb_command('showioalloc') 802def ShowIOAllocations(cmd_args=None): 803 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details. 804 Routine to display a summary of memory accounting allocated by IOKit allocators. 805 """ 806 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024)) 807 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024)) 808 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024)) 809 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024)) 810 811 812# EndMacro: showioalloc 813 814 815 816 817# Macro: showtaskvme 818@lldb_command('showtaskvme') 819def ShowTaskVmeHelper(cmd_args=None): 820 """ Display a summary list of the specified vm_map's entries 821 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 ) 822 """ 823 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 824 ShowTaskVMEntries(task) 825 826@lldb_command('showallvme') 827def ShowAllVME(cmd_args=None): 828 """ Routine to print a summary listing of all the vm map entries 829 Go Through each task in system and show the vm info 830 """ 831 for task in kern.tasks: 832 ShowTaskVMEntries(task) 833 834@lldb_command('showallvm') 835def ShowAllVM(cmd_args=None): 836 """ Routine to print a summary listing of all the vm maps 837 """ 838 for task in kern.tasks: 839 print GetTaskSummary.header + ' ' + GetProcSummary.header 840 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *')) 841 print GetVMMapSummary.header 842 print GetVMMapSummary(task.map) 843 844@lldb_command("showtaskvm") 845def ShowTaskVM(cmd_args=None): 846 """ Display info about the specified task's vm_map 847 syntax: (lldb) showtaskvm <task_ptr> 848 """ 849 if not cmd_args: 850 print ShowTaskVM.__doc__ 851 return False 852 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 853 if not task: 854 print "Unknown arguments." 855 return False 856 print GetTaskSummary.header + ' ' + GetProcSummary.header 857 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *')) 858 print GetVMMapSummary.header 859 print GetVMMapSummary(task.map) 860 return True 861 862@lldb_command('showallvmstats') 863def ShowAllVMStats(cmd_args=None): 864 """ Print a summary of vm statistics in a table format 865 """ 866 vmstats = lambda:None 867 vmstats.wired_count = 0 868 vmstats.resident_count = 0 869 vmstats.resident_max = 0 870 vmstats.internal = 0 871 vmstats.external = 0 872 vmstats.reusable = 0 873 vmstats.compressed = 0 874 vmstats.compressed_peak = 0 875 vmstats.compressed_lifetime = 0 876 vmstats.error = '' 877 878 hdr_format = "{0: >10s} {1: <20s} {2: >6s} {3: >10s} {4: >10s} {5: >10s} {6: >10s} {7: >10s} {8: >10s} {9: >10s} {10: >10s} {11: >10s} {12: >10s} {13: >10s} {14:}" 879 print hdr_format.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '') 880 print hdr_format.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '') 881 entry_format = "{p.p_pid: >10d} {p.p_comm: <20s} {m.hdr.nentries: >6d} {s.wired_count: >10d} {vsize: >10d} {s.resident_count: >10d} {s.new_resident_count: >10d} {s.resident_max: >10d} {s.internal: >10d} {s.external: >10d} {s.reusable: >10d} {s.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {s.error}" 882 883 for task in kern.tasks: 884 proc = Cast(task.bsd_info, 'proc *') 885 vmmap = Cast(task.map, '_vm_map *') 886 vmstats.error = '' 887 vmstats.wired_count = vmmap.pmap.stats.wired_count; 888 vmstats.resident_count = unsigned(vmmap.pmap.stats.resident_count); 889 vmstats.resident_max = vmmap.pmap.stats.resident_max; 890 vmstats.internal = unsigned(vmmap.pmap.stats.internal); 891 vmstats.external = unsigned(vmmap.pmap.stats.external); 892 vmstats.reusable = unsigned(vmmap.pmap.stats.reusable); 893 vmstats.compressed = unsigned(vmmap.pmap.stats.compressed); 894 vmstats.compressed_peak = unsigned(vmmap.pmap.stats.compressed_peak); 895 vmstats.compressed_lifetime = unsigned(vmmap.pmap.stats.compressed_lifetime); 896 vmstats.new_resident_count = vmstats.internal + vmstats.external 897 898 if vmstats.internal < 0: 899 vmstats.error += '*' 900 if vmstats.external < 0: 901 vmstats.error += '*' 902 if vmstats.reusable < 0: 903 vmstats.error += '*' 904 if vmstats.compressed < 0: 905 vmstats.error += '*' 906 if vmstats.compressed_peak < 0: 907 vmstats.error += '*' 908 if vmstats.compressed_lifetime < 0: 909 vmstats.error += '*' 910 if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count: 911 vmstats.error += '*' 912 913 print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) >> 12), t=task, s=vmstats) 914 915 916def ShowTaskVMEntries(task): 917 """ Routine to print out a summary listing of all the entries in a vm_map 918 params: 919 task - core.value : a object of type 'task *' 920 returns: 921 None 922 """ 923 print "vm_map entries for task " + hex(task) 924 print GetTaskSummary.header 925 print GetTaskSummary(task) 926 if not task.map: 927 print "Task {0: <#020x} has map = 0x0" 928 return None 929 print GetVMMapSummary.header 930 print GetVMMapSummary(task.map) 931 vme_list_head = task.map.hdr.links 932 vme_ptr_type = GetType('vm_map_entry *') 933 print GetVMEntrySummary.header 934 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 935 print GetVMEntrySummary(vme) 936 return None 937 938@lldb_command("showmap") 939def ShowMap(cmd_args=None): 940 """ Routine to print out info about the specified vm_map 941 usage: showmap <vm_map> 942 """ 943 if cmd_args == None or len(cmd_args) < 1: 944 print "Invalid argument.", ShowMap.__doc__ 945 return 946 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 947 print GetVMMapSummary.header 948 print GetVMMapSummary(map_val) 949 950@lldb_command("showmapvme") 951def ShowMapVME(cmd_args=None): 952 """Routine to print out info about the specified vm_map and its vm entries 953 usage: showmapvme <vm_map> 954 """ 955 if cmd_args == None or len(cmd_args) < 1: 956 print "Invalid argument.", ShowMap.__doc__ 957 return 958 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 959 print GetVMMapSummary.header 960 print GetVMMapSummary(map_val) 961 vme_list_head = map_val.hdr.links 962 vme_ptr_type = GetType('vm_map_entry *') 963 print GetVMEntrySummary.header 964 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 965 print GetVMEntrySummary(vme) 966 return None 967 968@lldb_type_summary(['_vm_map *', 'vm_map_t']) 969@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free")) 970def GetVMMapSummary(vmmap): 971 """ Display interesting bits from vm_map struct """ 972 out_string = "" 973 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}" 974 vm_size = uint64_t(vmmap.size).value 975 resident_pages = 0 976 if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count) 977 out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, vmmap.first_free) 978 return out_string 979 980@lldb_type_summary(['vm_map_entry']) 981@header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset")) 982def GetVMEntrySummary(vme): 983 """ Display vm entry specific information. """ 984 out_string = "" 985 format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}" 986 vme_protection = int(vme.protection) 987 vme_max_protection = int(vme.max_protection) 988 vme_extra_info_str ="SC-Ds"[int(vme.inheritance)] 989 if int(vme.is_sub_map) != 0 : 990 vme_extra_info_str +="s" 991 elif int(vme.needs_copy) != 0 : 992 vme_extra_info_str +="n" 993 num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) >> 12 994 out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.object.vm_object, vme.offset) 995 return out_string 996 997# EndMacro: showtaskvme 998@lldb_command('showmapwired') 999def ShowMapWired(cmd_args=None): 1000 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map 1001 """ 1002 if cmd_args == None or len(cmd_args) < 1: 1003 print "Invalid argument", ShowMapWired.__doc__ 1004 return 1005 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 1006 1007 1008@lldb_type_summary(['kmod_info_t *']) 1009@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: >20s} {6: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'version', 'name')) 1010def GetKextSummary(kmod): 1011 """ returns a string representation of kext information 1012 """ 1013 out_string = "" 1014 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: >20s} {6: <30s}" 1015 out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, kmod.version, kmod.name) 1016 return out_string 1017 1018@lldb_type_summary(['uuid_t']) 1019@header("") 1020def GetUUIDSummary(uuid): 1021 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26 1022 """ 1023 arr = Cast(addressof(uuid), 'uint8_t *') 1024 data = [] 1025 for i in range(16): 1026 data.append(int(arr[i])) 1027 return "{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}".format(a=data) 1028 1029@lldb_command('showallkmods') 1030def ShowAllKexts(cmd_args=None): 1031 """Display a summary listing of all loaded kexts (alias: showallkmods) 1032 """ 1033 kmod_val = kern.globals.kmod 1034 print "{: <36s} ".format("UUID") + GetKextSummary.header 1035 kextuuidinfo = GetKextLoadInformation() 1036 for kval in IterateLinkedList(kmod_val, 'next'): 1037 uuid = "........-....-....-....-............" 1038 kaddr = unsigned(kval.address) 1039 for l in kextuuidinfo : 1040 if kaddr == int(l[1],16): 1041 uuid = l[0] 1042 break 1043 print uuid + " " + GetKextSummary(kval) 1044 1045def GetKextLoadInformation(addr=0): 1046 """ Extract the kext uuid and load address information from the kernel data structure. 1047 params: 1048 addr - int - optional integer that is the address to search for. 1049 returns: 1050 [] - array with each entry of format ( 'UUID', 'Hex Load Address') 1051 """ 1052 # because of <rdar://problem/12683084>, we can't find summaries directly 1053 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries)) 1054 baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10 1055 summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *') 1056 total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries) 1057 kext_version = int(kern.globals.gLoadedKextSummaries.version) 1058 entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4 1059 if kext_version >= 2 : 1060 entry_size = int(kern.globals.gLoadedKextSummaries.entry_size) 1061 retval = [] 1062 for i in range(total_summaries): 1063 tmpaddress = unsigned(summaries_begin) + (i * entry_size) 1064 current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *') 1065 if addr != 0 : 1066 if addr == unsigned(current_kext.address): 1067 retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) )) 1068 else: 1069 retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) )) 1070 1071 return retval 1072 1073lldb_alias('showallkexts', 'showallkmods') 1074 1075def GetOSKextVersion(version_num): 1076 """ returns a string of format 1.2.3x from the version_num 1077 params: version_num - int 1078 return: str 1079 """ 1080 if version_num == -1 : 1081 return "invalid" 1082 (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000) 1083 version = version_num 1084 1085 vers_major = version / MAJ_MULT 1086 version = version - (vers_major * MAJ_MULT) 1087 1088 vers_minor = version / MIN_MULT 1089 version = version - (vers_minor * MIN_MULT) 1090 1091 vers_revision = version / REV_MULT 1092 version = version - (vers_revision * REV_MULT) 1093 1094 vers_stage = version / STAGE_MULT 1095 version = version - (vers_stage * STAGE_MULT) 1096 1097 vers_stage_level = version 1098 1099 out_str = "%d.%d" % (vers_major, vers_minor) 1100 if vers_revision > 0: out_str += ".%d" % vers_revision 1101 if vers_stage == 1 : out_str += "d%d" % vers_stage_level 1102 if vers_stage == 3 : out_str += "a%d" % vers_stage_level 1103 if vers_stage == 5 : out_str += "b%d" % vers_stage_level 1104 if vers_stage == 6 : out_str += "fc%d" % vers_stage_level 1105 1106 return out_str 1107 1108@lldb_command('showallknownkmods') 1109def ShowAllKnownKexts(cmd_args=None): 1110 """ Display a summary listing of all kexts known in the system. 1111 This is particularly useful to find if some kext was unloaded before this crash'ed state. 1112 """ 1113 kext_count = int(kern.globals.sKextsByID.count) 1114 index = 0 1115 kext_dictionary = kern.globals.sKextsByID.dictionary 1116 print "%d kexts in sKextsByID:" % kext_count 1117 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name') 1118 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}" 1119 1120 while index < kext_count: 1121 kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index) 1122 kext_name = str(kext_dict.key.string) 1123 osk = Cast(kext_dict.value, 'OSKext *') 1124 if int(osk.flags.loaded) : 1125 load_addr = "{0: <#020x}".format(osk.kmod_info) 1126 id = "{0: >5d}".format(osk.loadTag) 1127 else: 1128 load_addr = "------" 1129 id = "--" 1130 version_num = unsigned(osk.version) 1131 version = GetOSKextVersion(version_num) 1132 print format_string.format(osk, load_addr, id, version, kext_name) 1133 index += 1 1134 1135 return 1136 1137@lldb_command('showkmodaddr') 1138def ShowKmodAddr(cmd_args=[]): 1139 """ Given an address, print the offset and name for the kmod containing it 1140 Syntax: (lldb) showkmodaddr <addr> 1141 """ 1142 if len(cmd_args) < 1: 1143 raise ArgumentError("Insufficient arguments") 1144 1145 addr = ArgumentStringToInt(cmd_args[0]) 1146 kmod_val = kern.globals.kmod 1147 for kval in IterateLinkedList(kmod_val, 'next'): 1148 if addr >= unsigned(kval.address) and addr <= (unsigned(kval.address) + unsigned(kval.size)): 1149 print GetKextSummary.header 1150 print GetKextSummary(kval) + " offset = {0: #0x}".format((addr - unsigned(kval.address))) 1151 return True 1152 return False 1153 1154@lldb_command('addkext','F:N:') 1155def AddKextSyms(cmd_args=[], cmd_options={}): 1156 """ Add kext symbols into lldb. 1157 This command finds symbols for a uuid and load the required executable 1158 Usage: 1159 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E 1160 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address 1161 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto 1162 addkext all : Will load all the kext symbols - SLOW 1163 """ 1164 1165 1166 if "-F" in cmd_options: 1167 exec_path = cmd_options["-F"] 1168 exec_full_path = ResolveFSPath(exec_path) 1169 if not os.path.exists(exec_full_path): 1170 raise ArgumentError("Unable to resolve {:s}".format(exec_path)) 1171 1172 if not os.path.isfile(exec_full_path): 1173 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\ 1174\nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\ 1175\nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path)) 1176 if not os.access(exec_full_path, os.X_OK): 1177 raise ArgumentError("Path is {:s} not an executable file".format(exec_path)) 1178 1179 slide_value = None 1180 if cmd_args: 1181 slide_value = cmd_args[0] 1182 debuglog("loading slide value from user input %s" % cmd_args[0]) 1183 1184 filespec = lldb.SBFileSpec(exec_full_path, False) 1185 print "target modules add %s" % exec_full_path 1186 print lldb_run_command("target modules add %s" % exec_full_path) 1187 loaded_module = LazyTarget.GetTarget().FindModule(filespec) 1188 if loaded_module.IsValid(): 1189 uuid_str = loaded_module.GetUUIDString() 1190 debuglog("added module %s with uuid %s" % (exec_full_path, uuid_str)) 1191 if slide_value is None: 1192 all_kexts_info = GetKextLoadInformation() 1193 for k in all_kexts_info: 1194 debuglog(k[0]) 1195 if k[0].lower() == uuid_str.lower(): 1196 slide_value = k[1] 1197 debuglog("found the slide %s for uuid %s" % (k[1], k[0])) 1198 1199 if slide_value is None: 1200 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path) 1201 load_cmd = "target modules load --file %s --slide %s" % (exec_full_path, str(slide_value)) 1202 print load_cmd 1203 print lldb_run_command(load_cmd) 1204 kern.symbolicator = None 1205 return True 1206 1207 all_kexts_info = GetKextLoadInformation() 1208 1209 if "-N" in cmd_options: 1210 kext_name = cmd_options["-N"] 1211 kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True) 1212 if len(kext_name_matches) != 1: 1213 print "Ambiguous match for name: {:s}".format(kext_name) 1214 if len(kext_name_matches) > 0: 1215 print "Options are:\n\t" + "\n\t".join(kext_name_matches) 1216 return 1217 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name)) 1218 for x in all_kexts_info: 1219 if kext_name_matches[0] == x[2]: 1220 cur_uuid = x[0].lower() 1221 print "Fetching dSYM for {:s}".format(cur_uuid) 1222 info = dsymForUUID(cur_uuid) 1223 if info and 'DBGSymbolRichExecutable' in info: 1224 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable']) 1225 addDSYM(cur_uuid, info) 1226 loadDSYM(cur_uuid, int(x[1],16)) 1227 else: 1228 print "Failed to get symbol info for {:s}".format(cur_uuid) 1229 break 1230 kern.symbolicator = None 1231 return 1232 1233 if len(cmd_args) < 1: 1234 raise ArgumentError("No arguments specified.") 1235 1236 uuid = cmd_args[0].lower() 1237 1238 load_all_kexts = False 1239 if uuid == "all": 1240 load_all_kexts = True 1241 1242 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0: 1243 raise ArgumentError("Unknown argument {:s}".format(uuid)) 1244 1245 for k_info in all_kexts_info: 1246 cur_uuid = k_info[0].lower() 1247 if load_all_kexts or (uuid == cur_uuid): 1248 print "Fetching dSYM for %s" % cur_uuid 1249 info = dsymForUUID(cur_uuid) 1250 if info and 'DBGSymbolRichExecutable' in info: 1251 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable']) 1252 addDSYM(cur_uuid, info) 1253 loadDSYM(cur_uuid, int(k_info[1],16)) 1254 else: 1255 print "Failed to get symbol info for %s" % cur_uuid 1256 #end of for loop 1257 kern.symbolicator = None 1258 return True 1259 1260 1261 1262lldb_alias('showkmod', 'showkmodaddr') 1263lldb_alias('showkext', 'showkmodaddr') 1264lldb_alias('showkextaddr', 'showkmodaddr') 1265 1266@lldb_type_summary(['mount *']) 1267@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom')) 1268def GetMountSummary(mount): 1269 """ Display a summary of mount on the system 1270 """ 1271 out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " + 1272 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " + 1273 "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s}").format(mnt=mount, vfs=mount.mnt_vfsstat) 1274 return out_string 1275 1276@lldb_command('showallmounts') 1277def ShowAllMounts(cmd_args=None): 1278 """ Print all mount points 1279 """ 1280 mntlist = kern.globals.mountlist 1281 print GetMountSummary.header 1282 for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'): 1283 print GetMountSummary(mnt) 1284 return 1285 1286lldb_alias('ShowAllVols', 'showallmounts') 1287 1288@lldb_command('systemlog') 1289def ShowSystemLog(cmd_args=None): 1290 """ Display the kernel's printf ring buffer """ 1291 msgbufp = kern.globals.msgbufp 1292 msg_size = int(msgbufp.msg_size) 1293 msg_bufx = int(msgbufp.msg_bufx) 1294 msg_bufr = int(msgbufp.msg_bufr) 1295 msg_bufc = msgbufp.msg_bufc 1296 msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size) 1297 1298 # the buffer is circular; start at the write pointer to end, 1299 # then from beginning to write pointer 1300 line = '' 1301 err = lldb.SBError() 1302 for i in range(msg_bufx, msg_size) + range(0, msg_bufx) : 1303 err.Clear() 1304 cbyte = msg_bufc_data.GetUnsignedInt8(err, i) 1305 if not err.Success() : 1306 raise ValueError("Failed to read character at offset " + i + ": " + err.GetCString()) 1307 c = chr(cbyte) 1308 if c == '\0' : 1309 continue 1310 elif c == '\n' : 1311 print line 1312 line = '' 1313 else : 1314 line += c 1315 1316 if len(line) > 0 : 1317 print line 1318 1319 return 1320 1321@static_var('output','') 1322def _GetVnodePathName(vnode, vnodename): 1323 """ Internal function to get vnode path string from vnode structure. 1324 params: 1325 vnode - core.value 1326 vnodename - str 1327 returns Nothing. The output will be stored in the static variable. 1328 """ 1329 if not vnode: 1330 return 1331 if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0: 1332 if int(vnode.v_mount.mnt_vnodecovered): 1333 _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) ) 1334 else: 1335 _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name)) 1336 _GetVnodePathName.output += "/%s" % vnodename 1337 1338def GetVnodePath(vnode): 1339 """ Get string representation of the vnode 1340 params: vnodeval - value representing vnode * in the kernel 1341 return: str - of format /path/to/something 1342 """ 1343 out_str = '' 1344 if vnode: 1345 if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) : 1346 out_str += "/" 1347 else: 1348 _GetVnodePathName.output = '' 1349 if abs(vnode.v_name) != 0: 1350 _GetVnodePathName(vnode, str(vnode.v_name)) 1351 out_str += _GetVnodePathName.output 1352 else: 1353 out_str += 'v_name = NULL' 1354 _GetVnodePathName.output = '' 1355 return out_str 1356 1357 1358@lldb_command('showvnodepath') 1359def ShowVnodePath(cmd_args=None): 1360 """ Prints the path for a vnode 1361 usage: showvnodepath <vnode> 1362 """ 1363 if cmd_args != None and len(cmd_args) > 0 : 1364 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 1365 if vnode_val: 1366 print GetVnodePath(vnode_val) 1367 return 1368 1369# Macro: showvnodedev 1370def GetVnodeDevInfo(vnode): 1371 """ Internal function to get information from the device type vnodes 1372 params: vnode - value representing struct vnode * 1373 return: str - formatted output information for block and char vnode types passed as param 1374 """ 1375 vnodedev_output = "" 1376 vblk_type = GetEnumValue('vtype::VBLK') 1377 vchr_type = GetEnumValue('vtype::VCHR') 1378 if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type): 1379 devnode = Cast(vnode.v_data, 'devnode_t *') 1380 devnode_dev = devnode.dn_typeinfo.dev 1381 devnode_major = (devnode_dev >> 24) & 0xff 1382 devnode_minor = devnode_dev & 0x00ffffff 1383 1384 # boilerplate device information for a vnode 1385 vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode) 1386 vnodedev_output += "\n\t type:\t\t" 1387 if (vnode.v_type == vblk_type): 1388 vnodedev_output += "VBLK" 1389 if (vnode.v_type == vchr_type): 1390 vnodedev_output += "VCHR" 1391 vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name) 1392 vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor) 1393 vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode)) 1394 vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid) 1395 1396 # decode device specific data 1397 vnodedev_output += "\nDevice Specific Information:\t" 1398 if (vnode.v_type == vblk_type): 1399 vnodedev_output += "Sorry, I do not know how to decode block devices yet!" 1400 vnodedev_output += "\nMaybe you can write me!" 1401 1402 if (vnode.v_type == vchr_type): 1403 # Device information; this is scanty 1404 # range check 1405 if (devnode_major > 42) or (devnode_major < 0): 1406 vnodedev_output += "Invalid major #\n" 1407 # static assignments in conf 1408 elif (devnode_major == 0): 1409 vnodedev_output += "Console mux device\n" 1410 elif (devnode_major == 2): 1411 vnodedev_output += "Current tty alias\n" 1412 elif (devnode_major == 3): 1413 vnodedev_output += "NULL device\n" 1414 elif (devnode_major == 4): 1415 vnodedev_output += "Old pty slave\n" 1416 elif (devnode_major == 5): 1417 vnodedev_output += "Old pty master\n" 1418 elif (devnode_major == 6): 1419 vnodedev_output += "Kernel log\n" 1420 elif (devnode_major == 12): 1421 vnodedev_output += "Memory devices\n" 1422 # Statically linked dynamic assignments 1423 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')): 1424 vnodedev_output += "Cloning pty master not done\n" 1425 #GetVnodeDevCpty(devnode_major, devnode_minor) 1426 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')): 1427 vnodedev_output += "Cloning pty slave not done\n" 1428 #GetVnodeDevCpty(devnode_major, devnode_minor) 1429 else: 1430 vnodedev_output += "RESERVED SLOT\n" 1431 else: 1432 vnodedev_output += "{:#x} is not a device".format(vnode) 1433 return vnodedev_output 1434 1435@lldb_command('showvnodedev') 1436def ShowVnodeDev(cmd_args=None): 1437 """ Routine to display details of all vnodes of block and character device types 1438 Usage: showvnodedev <address of vnode> 1439 """ 1440 if not cmd_args: 1441 print "No arguments passed" 1442 print ShowVnodeDev.__doc__ 1443 return False 1444 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 1445 if not vnode_val: 1446 print "unknown arguments:", str(cmd_args) 1447 return False 1448 print GetVnodeDevInfo(vnode_val) 1449 1450# EndMacro: showvnodedev 1451 1452# Macro: showvnodelocks 1453def GetVnodeLock(lockf): 1454 """ Internal function to get information from the given advisory lock 1455 params: lockf - value representing v_lockf member in struct vnode * 1456 return: str - formatted output information for the advisory lock 1457 """ 1458 vnode_lock_output = '' 1459 lockf_flags = lockf.lf_flags 1460 lockf_type = lockf.lf_type 1461 if lockf_flags & 0x20: 1462 vnode_lock_output += ("{: <8s}").format('flock') 1463 if lockf_flags & 0x40: 1464 vnode_lock_output += ("{: <8s}").format('posix') 1465 if lockf_flags & 0x80: 1466 vnode_lock_output += ("{: <8s}").format('prov') 1467 if lockf_flags & 0x10: 1468 vnode_lock_output += ("{: <4s}").format('W') 1469 else: 1470 vnode_lock_output += ("{: <4s}").format('.') 1471 1472 # POSIX file vs advisory range locks 1473 if lockf_flags & 0x40: 1474 lockf_proc = Cast(lockf.lf_id, 'proc *') 1475 vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid) 1476 else: 1477 vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id)) 1478 1479 # lock type 1480 if lockf_type == 1: 1481 vnode_lock_output += ("{: <12s}").format('shared') 1482 else: 1483 if lockf_type == 3: 1484 vnode_lock_output += ("{: <12s}").format('exclusive') 1485 else: 1486 if lockf_type == 2: 1487 vnode_lock_output += ("{: <12s}").format('unlock') 1488 else: 1489 vnode_lock_output += ("{: <12s}").format('unknown') 1490 1491 # start and stop values 1492 vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start) 1493 vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end) 1494 return vnode_lock_output 1495 1496@header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end')) 1497def GetVnodeLocksSummary(vnode): 1498 """ Internal function to get summary of advisory locks for the given vnode 1499 params: vnode - value representing the vnode object 1500 return: str - formatted output information for the summary of advisory locks 1501 """ 1502 out_str = '' 1503 if vnode: 1504 lockf_list = vnode.v_lockf 1505 for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'): 1506 out_str += ("{: <4s}").format('H') 1507 out_str += GetVnodeLock(lockf_itr) 1508 lockf_blocker = lockf_itr.lf_blkhd.tqh_first 1509 while lockf_blocker: 1510 out_str += ("{: <4s}").format('>') 1511 out_str += GetVnodeLock(lockf_blocker) 1512 lockf_blocker = lockf_blocker.lf_block.tqe_next 1513 return out_str 1514 1515@lldb_command('showvnodelocks') 1516def ShowVnodeLocks(cmd_args=None): 1517 """ Routine to display list of advisory record locks for the given vnode address 1518 Usage: showvnodelocks <address of vnode> 1519 """ 1520 if not cmd_args: 1521 print "No arguments passed" 1522 print ShowVnodeLocks.__doc__ 1523 return False 1524 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 1525 if not vnode_val: 1526 print "unknown arguments:", str(cmd_args) 1527 return False 1528 print GetVnodeLocksSummary.header 1529 print GetVnodeLocksSummary(vnode_val) 1530 1531# EndMacro: showvnodelocks 1532 1533# Macro: showproclocks 1534 1535@lldb_command('showproclocks') 1536def ShowProcLocks(cmd_args=None): 1537 """ Routine to display list of advisory record locks for the given process 1538 Usage: showproclocks <address of proc> 1539 """ 1540 if not cmd_args: 1541 print "No arguments passed" 1542 print ShowProcLocks.__doc__ 1543 return False 1544 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *') 1545 if not proc: 1546 print "unknown arguments:", str(cmd_args) 1547 return False 1548 out_str = '' 1549 proc_filedesc = proc.p_fd 1550 fd_lastfile = proc_filedesc.fd_lastfile 1551 fd_ofiles = proc_filedesc.fd_ofiles 1552 count = 0 1553 seen = 0 1554 while count <= fd_lastfile: 1555 if fd_ofiles[count]: 1556 fglob = fd_ofiles[count].f_fglob 1557 fo_type = fglob.fg_ops.fo_type 1558 if fo_type == 1: 1559 fg_data = fglob.fg_data 1560 fg_vnode = Cast(fg_data, 'vnode *') 1561 name = fg_vnode.v_name 1562 lockf_itr = fg_vnode.v_lockf 1563 if lockf_itr: 1564 if not seen: 1565 print GetVnodeLocksSummary.header 1566 seen = seen + 1 1567 out_str += ("\n( fd {:d}, name ").format(count) 1568 if not name: 1569 out_str += "(null) )\n" 1570 else: 1571 out_str += "{:s} )\n".format(name) 1572 print out_str 1573 print GetVnodeLocksSummary(fg_vnode) 1574 count = count + 1 1575 print "\n{0: d} total locks for {1: #018x}".format(seen, proc) 1576 1577# EndMacro: showproclocks 1578 1579@lldb_type_summary(['vnode_t', 'vnode *']) 1580@header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'name')) 1581def GetVnodeSummary(vnode): 1582 """ Get a summary of important information out of vnode 1583 """ 1584 out_str = '' 1585 format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <35s}" 1586 usecount = int(vnode.v_usecount) 1587 iocount = int(vnode.v_iocount) 1588 v_data_ptr = int(hex(vnode.v_data), 16) 1589 vtype = int(vnode.v_type) 1590 vtype_str = "%d" % vtype 1591 vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition 1592 if vtype >= 0 and vtype < len(vnode_types): 1593 vtype_str = vnode_types[vtype] 1594 parent_ptr = int(hex(vnode.v_parent), 16) 1595 name_ptr = int(hex(vnode.v_name), 16) 1596 name ="" 1597 if name_ptr != 0: 1598 name = str(vnode.v_name) 1599 elif int(vnode.v_tag) == 16 : 1600 cnode = Cast(vnode.v_data, 'cnode *') 1601 name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *')) 1602 mapped = '-' 1603 if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0): 1604 # Check to see if vnode is mapped/unmapped 1605 if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0: 1606 mapped = '1' 1607 else: 1608 mapped = '0' 1609 out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, name) 1610 return out_str 1611 1612@lldb_command('showallvnodes') 1613def ShowAllVnodes(cmd_args=None): 1614 """ Display info about all vnodes 1615 """ 1616 mntlist = kern.globals.mountlist 1617 print GetVnodeSummary.header 1618 for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'): 1619 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 1620 print GetVnodeSummary(vnodeval) 1621 return 1622 1623@lldb_command('showvnode') 1624def ShowVnode(cmd_args=None): 1625 """ Display info about one vnode 1626 usage: showvnode <vnode> 1627 """ 1628 if cmd_args == None or len(cmd_args) < 1: 1629 print "Please provide valid vnode argument. Type help showvnode for help." 1630 return 1631 vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *') 1632 print GetVnodeSummary.header 1633 print GetVnodeSummary(vnodeval) 1634 1635@lldb_command('showvolvnodes') 1636def ShowVolVnodes(cmd_args=None): 1637 """ Display info about all vnodes of a given mount_t 1638 """ 1639 if cmd_args == None or len(cmd_args) < 1: 1640 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help" 1641 return 1642 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t') 1643 print GetVnodeSummary.header 1644 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 1645 print GetVnodeSummary(vnodeval) 1646 return 1647 1648@lldb_command('showvolbusyvnodes') 1649def ShowVolBusyVnodes(cmd_args=None): 1650 """ Display info about busy (iocount!=0) vnodes of a given mount_t 1651 """ 1652 if cmd_args == None or len(cmd_args) < 1: 1653 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help" 1654 return 1655 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t') 1656 print GetVnodeSummary.header 1657 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 1658 if int(vnodeval.v_iocount) != 0: 1659 print GetVnodeSummary(vnodeval) 1660 1661@lldb_command('showallbusyvnodes') 1662def ShowAllBusyVnodes(cmd_args=None): 1663 """ Display info about all busy (iocount!=0) vnodes 1664 """ 1665 mntlistval = kern.globals.mountlist 1666 for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'): 1667 ShowVolBusyVnodes([hex(mntval)]) 1668 1669@lldb_command('print_vnode') 1670def PrintVnode(cmd_args=None): 1671 """ Prints out the fields of a vnode struct 1672 Usage: print_vnode <vnode> 1673 """ 1674 if not cmd_args: 1675 print "Please provide valid vnode argument. Type help print_vnode for help." 1676 return 1677 ShowVnode(cmd_args) 1678 1679@lldb_command('showworkqvnodes') 1680def ShowWorkqVnodes(cmd_args=None): 1681 """ Print the vnode worker list 1682 Usage: showworkqvnodes <struct mount *> 1683 """ 1684 if not cmd_args: 1685 print "Please provide valid mount argument. Type help showworkqvnodes for help." 1686 return 1687 1688 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *') 1689 vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *') 1690 print GetVnodeSummary.header 1691 while int(vp) != 0: 1692 print GetVnodeSummary(vp) 1693 vp = vp.v_mntvnodes.tqe_next 1694 1695@lldb_command('shownewvnodes') 1696def ShowNewVnodes(cmd_args=None): 1697 """ Print the new vnode list 1698 Usage: shownewvnodes <struct mount *> 1699 """ 1700 if not cmd_args: 1701 print "Please provide valid mount argument. Type help shownewvnodes for help." 1702 return 1703 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *') 1704 vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *') 1705 print GetVnodeSummary.header 1706 while int(vp) != 0: 1707 print GetVnodeSummary(vp) 1708 vp = vp.v_mntvnodes.tqe_next 1709 1710 1711@lldb_command('showprocvnodes') 1712def ShowProcVnodes(cmd_args=None): 1713 """ Routine to print out all the open fds which are vnodes in a process 1714 Usage: showprocvnodes <proc *> 1715 """ 1716 if not cmd_args: 1717 print "Please provide valid proc argument. Type help showprocvnodes for help." 1718 return 1719 procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *') 1720 fdptr = Cast(procptr.p_fd, 'filedesc *') 1721 if int(fdptr.fd_cdir) != 0: 1722 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir)) 1723 if int(fdptr.fd_rdir) != 0: 1724 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir)) 1725 count = 0 1726 print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary.header 1727 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers 1728 fpptr = Cast(fdptr.fd_ofiles, 'fileproc *') 1729 while count < fdptr.fd_nfiles: 1730 fpp = dereference(fpptr) 1731 fproc = Cast(fpp, 'fileproc *') 1732 if int(fproc) != 0: 1733 fglob = dereference(fproc).f_fglob 1734 flags = "" 1735 if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1): 1736 if (fdptr.fd_ofileflags[count] & 1): flags += 'E' 1737 if (fdptr.fd_ofileflags[count] & 2): flags += 'F' 1738 if (fdptr.fd_ofileflags[count] & 4): flags += 'R' 1739 if (fdptr.fd_ofileflags[count] & 8): flags += 'C' 1740 print '{0: <5d} {1: <7s}'.format(count, flags) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *')) 1741 count += 1 1742 fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'fileproc *') 1743 1744@lldb_command('showallprocvnodes') 1745def ShowAllProcVnodes(cmd_args=None): 1746 """ Routine to print out all the open fds which are vnodes 1747 """ 1748 1749 procptr = Cast(kern.globals.allproc.lh_first, 'proc *') 1750 while procptr and int(procptr) != 0: 1751 print '{:<s}'.format("=" * 106) 1752 print GetProcInfo(procptr) 1753 ShowProcVnodes([int(procptr)]) 1754 procptr = procptr.p_list.le_next 1755 1756@xnudebug_test('test_vnode') 1757def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ): 1758 """ Test the functionality of vnode related commands 1759 returns 1760 - False on failure 1761 - True on success 1762 """ 1763 if not isConnected: 1764 print "Target is not connected. Cannot test memstats" 1765 return False 1766 res = lldb.SBCommandReturnObject() 1767 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res) 1768 result = res.GetOutput() 1769 if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5: 1770 return True 1771 else: 1772 return False 1773 1774# Macro: showallmtx 1775@lldb_type_summary(['_lck_grp_ *']) 1776def GetMutexEntry(mtxg): 1777 """ Summarize a mutex group entry with important information. 1778 params: 1779 mtxg: value - obj representing a mutex group in kernel 1780 returns: 1781 out_string - summary of the mutex group 1782 """ 1783 out_string = "" 1784 1785 if kern.ptrsize == 8: 1786 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 1787 else: 1788 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 1789 1790 if mtxg.lck_grp_mtxcnt: 1791 out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt, 1792 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt, 1793 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name) 1794 return out_string 1795 1796@lldb_command('showallmtx') 1797def ShowAllMtx(cmd_args=None): 1798 """ Routine to print a summary listing of all mutexes 1799 """ 1800 1801 if kern.ptrsize == 8: 1802 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 1803 else: 1804 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 1805 1806 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME') 1807 1808 mtxgrp_queue_head = kern.globals.lck_grp_queue 1809 mtxgrp_ptr_type = GetType('_lck_grp_ *') 1810 1811 for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"): 1812 print GetMutexEntry(mtxgrp_ptr) 1813 return 1814# EndMacro: showallmtx 1815 1816# Macro: showallrwlck 1817@lldb_type_summary(['_lck_grp_ *']) 1818def GetRWLEntry(rwlg): 1819 """ Summarize a reader writer lock group with important information. 1820 params: 1821 rwlg: value - obj representing a reader writer lock group in kernel 1822 returns: 1823 out_string - summary of the reader writer lock group 1824 """ 1825 out_string = "" 1826 1827 if kern.ptrsize == 8: 1828 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 1829 else: 1830 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 1831 1832 if rwlg.lck_grp_rwcnt: 1833 out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt, 1834 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt, 1835 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name) 1836 return out_string 1837 1838#Macro: showlock 1839@lldb_type_summary(['lck_mtx_t *']) 1840@header("===== Mutex Lock Summary =====") 1841def GetMutexLockSummary(mtx): 1842 """ Summarize mutex lock with important information. 1843 params: 1844 mtx: value - obj representing a mutex lock in kernel 1845 returns: 1846 out_str - summary of the mutex lock 1847 """ 1848 if not mtx: 1849 return "Invalid lock value: 0x0" 1850 1851 if kern.arch == "x86_64": 1852 out_str = "Lock Type\t\t: MUTEX\n" 1853 mtxd = mtx.lck_mtx_sw.lck_mtxd 1854 out_str += "Owner Thread\t\t: {:#x}\n".format(mtxd.lck_mtxd_owner) 1855 cmd_str = "p/d ((lck_mtx_t*){:#x})->lck_mtx_sw.lck_mtxd.".format(mtx) 1856 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_waiters") 1857 out_str += "Number of Waiters\t: {:s}\n".format(cmd_out.split()[-1]) 1858 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_ilocked") 1859 out_str += "ILocked\t\t\t: {:s}\n".format(cmd_out.split()[-1]) 1860 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_mlocked") 1861 out_str += "MLocked\t\t\t: {:s}\n".format(cmd_out.split()[-1]) 1862 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_promoted") 1863 out_str += "Promoted\t\t: {:s}\n".format(cmd_out.split()[-1]) 1864 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_spin") 1865 out_str += "Spin\t\t\t: {:s}\n".format(cmd_out.split()[-1]) 1866 return out_str 1867 1868 out_str = "Lock Type\t\t: MUTEX\n" 1869 out_str += "Owner Thread\t\t: {:#x}\n".format(mtx.lck_mtx_hdr.lck_mtxd_data & ~0x3) 1870 out_str += "Number of Waiters\t: {:d}\n".format(mtx.lck_mtx_sw.lck_mtxd.lck_mtxd_waiters) 1871 out_str += "Flags\t\t\t: " 1872 if mtx.lck_mtx_hdr.lck_mtxd_data & 0x1: 1873 out_str += "[Interlock Locked] " 1874 if mtx.lck_mtx_hdr.lck_mtxd_data & 0x2: 1875 out_str += "[Wait Flag]" 1876 if (mtx.lck_mtx_hdr.lck_mtxd_data & 0x3) == 0: 1877 out_str += "None" 1878 return out_str 1879 1880@lldb_type_summary(['lck_spin_t *']) 1881@header("===== SpinLock Summary =====") 1882def GetSpinLockSummary(spinlock): 1883 """ Summarize spinlock with important information. 1884 params: 1885 spinlock: value - obj representing a spinlock in kernel 1886 returns: 1887 out_str - summary of the spinlock 1888 """ 1889 if not spinlock: 1890 return "Invalid lock value: 0x0" 1891 1892 out_str = "Lock Type\t\t: SPINLOCK\n" 1893 if kern.arch == "x86_64": 1894 out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock) 1895 return out_str 1896 1897 out_str += "Owner Thread\t\t: {:#x}\n".format(spinlock.lck_spin_data & ~0x3) 1898 out_str += "Flags\t\t\t: " 1899 if spinlock.lck_spin_data & 0x1: 1900 out_str += "[Interlock Locked] " 1901 if spinlock.lck_spin_data & 0x2: 1902 out_str += "[Wait Flag]" 1903 if (spinlock.lck_spin_data & 0x3) == 0: 1904 out_str += "None" 1905 return out_str 1906 1907@lldb_command('showlock', 'MS') 1908def ShowLock(cmd_args=None, cmd_options={}): 1909 """ Show info about a lock - its state and owner thread details 1910 Usage: showlock <address of a lock> 1911 -M : to consider <addr> as lck_mtx_t 1912 -S : to consider <addr> as lck_spin_t 1913 """ 1914 if not cmd_args: 1915 raise ArgumentError("Please specify the address of the lock whose info you want to view.") 1916 return 1917 1918 summary_str = "" 1919 lock = kern.GetValueFromAddress(cmd_args[0], 'uintptr_t*') 1920 1921 if kern.arch == "x86_64" and lock: 1922 if "-M" in cmd_options: 1923 lock_mtx = Cast(lock, 'lck_mtx_t *') 1924 summary_str = GetMutexLockSummary(lock_mtx) 1925 elif "-S" in cmd_options: 1926 lock_spin = Cast(lock, 'lck_spin_t *') 1927 summary_str = GetSpinLockSummary(lock_spin) 1928 else: 1929 summary_str = "Please specify supported lock option(-M/-S)" 1930 1931 print summary_str 1932 return 1933 1934 if lock: 1935 lock_mtx = Cast(lock, 'lck_mtx_t*') 1936 if lock_mtx.lck_mtx_type == 0x22: 1937 summary_str = GetMutexLockSummary(lock_mtx) 1938 1939 lock_spin = Cast(lock, 'lck_spin_t*') 1940 if lock_spin.lck_spin_type == 0x11: 1941 summary_str = GetSpinLockSummary(lock_spin) 1942 1943 if summary_str == "": 1944 summary_str = "Lock Type\t\t: INVALID LOCK" 1945 print summary_str 1946 1947#EndMacro: showlock 1948 1949@lldb_command('showallrwlck') 1950def ShowAllRWLck(cmd_args=None): 1951 """ Routine to print a summary listing of all read/writer locks 1952 """ 1953 if kern.ptrsize == 8: 1954 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 1955 else: 1956 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 1957 1958 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME') 1959 1960 rwlgrp_queue_head = kern.globals.lck_grp_queue 1961 rwlgrp_ptr_type = GetType('_lck_grp_ *') 1962 for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"): 1963 print GetRWLEntry(rwlgrp_ptr) 1964 return 1965# EndMacro: showallrwlck 1966 1967#Macro: showbootermemorymap 1968@lldb_command('showbootermemorymap') 1969def ShowBooterMemoryMap(cmd_args=None): 1970 """ Prints out the phys memory map from kernelBootArgs 1971 Supported only on x86_64 1972 """ 1973 if kern.arch == 'x86_64': 1974 voffset = unsigned(0xFFFFFF8000000000) 1975 else: 1976 print "showbootermemorymap not supported on this architecture" 1977 return 1978 1979 out_string = "" 1980 1981 # Memory type map 1982 memtype_dict = { 1983 0: 'Reserved', 1984 1: 'LoaderCode', 1985 2: 'LoaderData', 1986 3: 'BS_code', 1987 4: 'BS_data', 1988 5: 'RT_code', 1989 6: 'RT_data', 1990 7: 'Convention', 1991 8: 'Unusable', 1992 9: 'ACPI_recl', 1993 10: 'ACPI_NVS', 1994 11: 'MemMapIO', 1995 12: 'MemPortIO', 1996 13: 'PAL_code' 1997 } 1998 1999 boot_args = kern.globals.kernelBootArgs 2000 msize = boot_args.MemoryMapDescriptorSize 2001 mcount = (boot_args.MemoryMapSize) / unsigned(msize) 2002 2003 out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes") 2004 2005 i = 0 2006 while i < mcount: 2007 mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + voffset + unsigned(i*msize), 'EfiMemoryRange *') 2008 mtype = unsigned(mptr.Type) 2009 if mtype in memtype_dict: 2010 out_string += "{0: <12s}".format(memtype_dict[mtype]) 2011 else: 2012 out_string += "{0: <12s}".format("UNKNOWN") 2013 2014 if mptr.VirtualStart == 0: 2015 out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute) 2016 else: 2017 out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute) 2018 i = i + 1 2019 2020 print out_string 2021#EndMacro: showbootermemorymap 2022 2023