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 7import shlex 8from utils import * 9import xnudefines 10from process import * 11import macho 12import json 13from ctypes import c_int64 14 15def vm_unpack_pointer(packed, params, type_str = 'void *'): 16 """ Unpack a pointer packed with VM_PACK_POINTER() 17 params: 18 packed - value : The packed pointer value 19 params - value : The packing parameters of type vm_packing_params_t 20 type_str - str : The type to cast the unpacked pointer into 21 returns: 22 The unpacked pointer 23 """ 24 if params.vmpp_base_relative: 25 addr = unsigned(packed) << int(params.vmpp_shift) 26 if addr: addr += int(params.vmpp_base) 27 else: 28 bits = int(params.vmpp_bits) 29 shift = int(params.vmpp_shift) 30 addr = c_int64(unsigned(packed) << (64 - bits)).value 31 addr >>= 64 - bits - shift 32 return kern.GetValueFromAddress(addr, type_str) 33 34def GetZPerCPU(root, cpu, element_type = None): 35 """ Iterates over a percpu variable 36 params: 37 root - value : Value object for per-cpu variable 38 cpu - int : the CPU number 39 element_type - str : Type of element 40 returns: 41 one slot 42 """ 43 pagesize = kern.globals.page_size 44 mangle = 1 << (8 * kern.ptrsize - 1) 45 if element_type is None: 46 element_type = root.GetSBValue().GetType() 47 return kern.GetValueFromAddress((int(root) | mangle) + cpu * pagesize, element_type) 48 49def IterateZPerCPU(root, element_type = None): 50 """ Iterates over a percpu variable 51 params: 52 root - value : Value object for per-cpu variable 53 element_type - str : Type of element 54 returns: 55 one slot 56 """ 57 for i in range(0, kern.globals.zpercpu_early_count): 58 yield GetZPerCPU(root, i, element_type) 59 60@lldb_command('showzpcpu', "S") 61def ShowZPerCPU(cmd_args=None, cmd_options={}): 62 """ Routine to show per-cpu zone allocated variables 63 64 Usage: showzpcpu [-S] expression [field] 65 -S : sum the values instead of printing them 66 """ 67 if not cmd_args: 68 raise ArgumentError("No arguments passed") 69 pagesize = kern.globals.page_size 70 mangle = 1 << (8 * kern.ptrsize - 1) 71 sbv = pagesize.GetSBValue() 72 v = sbv.CreateValueFromExpression(None, cmd_args[0]) 73 e = value(v) 74 acc = 0 75 for i in range(0, kern.globals.zpercpu_early_count): 76 if len(cmd_args) == 1: 77 t = sbv.CreateValueFromExpression(None, '(%s)%d' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize)).Dereference() 78 else: 79 t = sbv.CreateValueFromExpression(None, '((%s)%d)->%s' % (v.GetTypeName(), (int(e) | mangle) + i * pagesize, cmd_args[1])) 80 if "-S" in cmd_options: 81 acc += value(t) 82 else: 83 print value(t) 84 85 if "-S" in cmd_options: 86 print acc 87 88def ZoneName(zone, zone_security): 89 """ Formats the name for a given zone 90 params: 91 zone - value : A pointer to a zone 92 zone_security - value : A pointer to zone security flags 93 returns: 94 the formated name for the zone 95 """ 96 names = [ "", "default.", "data.", "kext."] 97 return "{:s}{:s}".format(names[int(zone_security.z_kheap_id)], zone.z_name) 98 99def GetZoneByName(name): 100 """ Internal function to find a zone by name 101 """ 102 for i in xrange(1, int(kern.GetGlobalVariable('num_zones'))): 103 z = addressof(kern.globals.zone_array[i]) 104 zs = addressof(kern.globals.zone_security_array[i]) 105 if ZoneName(z, zs) == name: 106 return z 107 return None 108 109def GetZoneCachedElements(zone): 110 """ Internal function to return cached element addresses in a zone 111 """ 112 cached = set() 113 page_size = unsigned(kern.globals.page_size) 114 115 def decode_element(addr): 116 base = unsigned(addr) & -page_size 117 idx = unsigned(addr) & (page_size - 1) 118 return base + unsigned(zone.z_elem_size) * idx 119 120 if zone.z_pcpu_cache: 121 for cache in IterateZPerCPU(zone.z_pcpu_cache): 122 for i in xrange(0, cache.zc_alloc_cur): 123 cached.add(decode_element(cache.zc_alloc_elems[i].ze_value)) 124 for i in xrange(0, cache.zc_free_cur): 125 cached.add(decode_element(cache.zc_free_elems[i].ze_value)) 126 for mag in IterateTAILQ_HEAD(cache.zc_depot, 'zm_link', 's'): 127 for i in xrange(0, mag.zm_cur): 128 cached.add(decode_element(mag.zm_elems[i].ze_value)) 129 130 return cached 131 132def IterateZoneElements(zone, minAddr=None, maxAddr=None): 133 """ Internal function to return allocated elements in a zone 134 """ 135 cached = GetZoneCachedElements(zone) 136 137 for q in [zone.z_pageq_full, zone.z_pageq_partial]: 138 for meta in ZoneIteratePageQueue(q): 139 for e in meta.iterateElements(): 140 if minAddr is not None and e < minAddr: 141 continue 142 if maxAddr is not None and e > maxAddr: 143 continue 144 if meta.isElementFree(e): 145 continue 146 if e in cached: 147 continue 148 yield e 149 150def PrettyPrintDictionary(d): 151 """ Internal function to pretty print a dictionary with string or integer values 152 params: The dictionary to print 153 """ 154 for key, value in d.items(): 155 key += ":" 156 if isinstance(value, int): 157 print "{:<30s} {: >10d}".format(key, value) 158 elif isinstance(value, long): 159 print "{:<30s} {: >10d}".format(key, value) 160 else: 161 print "{:<30s} {: >10s}".format(key, value) 162 163# Macro: memstats 164@lldb_command('memstats', 'J') 165def Memstats(cmd_args=None, cmd_options={}): 166 """ 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. 167 usage: memstats -J 168 Output json 169 """ 170 print_json = False 171 if "-J" in cmd_options: 172 print_json = True 173 174 memstats = {} 175 try: 176 memstats["memorystatus_level"] = int(kern.globals.memorystatus_level) 177 memstats["memorystatus_available_pages"] = int(kern.globals.memorystatus_available_pages) 178 memstats["inuse_ptepages_count"] = int(kern.globals.inuse_ptepages_count) 179 except ValueError: 180 pass 181 if hasattr(kern.globals, 'compressor_object'): 182 memstats["compressor_page_count"] = int(kern.globals.compressor_object.resident_page_count) 183 memstats["vm_page_throttled_count"] = int(kern.globals.vm_page_throttled_count) 184 memstats["vm_page_active_count"] = int(kern.globals.vm_page_active_count) 185 memstats["vm_page_inactive_count"] = int(kern.globals.vm_page_inactive_count) 186 memstats["vm_page_wire_count"] = int(kern.globals.vm_page_wire_count) 187 memstats["vm_page_free_count"] = int(kern.globals.vm_page_free_count) 188 memstats["vm_page_purgeable_count"] = int(kern.globals.vm_page_purgeable_count) 189 memstats["vm_page_inactive_target"] = int(kern.globals.vm_page_inactive_target) 190 memstats["vm_page_free_target"] = int(kern.globals.vm_page_free_target) 191 memstats["vm_page_free_reserved"] = int(kern.globals.vm_page_free_reserved) 192 193 # Serializing to json here ensure we always catch bugs preventing 194 # serialization 195 as_json = json.dumps(memstats) 196 if print_json: 197 print as_json 198 else: 199 PrettyPrintDictionary(memstats) 200 201@xnudebug_test('test_memstats') 202def TestMemstats(kernel_target, config, lldb_obj, isConnected ): 203 """ Test the functionality of memstats command 204 returns 205 - False on failure 206 - True on success 207 """ 208 if not isConnected: 209 print "Target is not connected. Cannot test memstats" 210 return False 211 res = lldb.SBCommandReturnObject() 212 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res) 213 result = res.GetOutput() 214 if result.split(":")[1].strip().find('None') == -1 : 215 return True 216 else: 217 return False 218 219# EndMacro: memstats 220 221# Macro: showmemorystatus 222def CalculateLedgerPeak(phys_footprint_entry): 223 """ Internal function to calculate ledger peak value for the given phys footprint entry 224 params: phys_footprint_entry - value representing struct ledger_entry * 225 return: value - representing the ledger peak for the given phys footprint entry 226 """ 227 return max(phys_footprint_entry['balance'], phys_footprint_entry.get('interval_max', 0)) 228 229@header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s} {: <32s}\n".format( 230'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint', 231'recent peak', 'lifemax', 'limit', 'command')) 232def GetMemoryStatusNode(proc_val): 233 """ Internal function to get memorystatus information from the given proc 234 params: proc - value representing struct proc * 235 return: str - formatted output information for proc object 236 """ 237 out_str = '' 238 task_val = Cast(proc_val.task, 'task *') 239 task_ledgerp = task_val.ledger 240 ledger_template = kern.globals.task_ledger_template 241 242 task_physmem_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_mem') 243 task_iokit_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'iokit_mapped') 244 task_phys_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_footprint') 245 page_size = kern.globals.page_size 246 247 phys_mem_footprint = task_physmem_footprint_ledger_entry['balance'] / page_size 248 iokit_footprint = task_iokit_footprint_ledger_entry['balance'] / page_size 249 phys_footprint = task_phys_footprint_ledger_entry['balance'] / page_size 250 phys_footprint_limit = task_phys_footprint_ledger_entry['limit'] / page_size 251 ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry) 252 phys_footprint_spike = ledger_peak / page_size 253 phys_footprint_lifetime_max = task_phys_footprint_ledger_entry['lifetime_max'] / page_size 254 255 format_string = '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}' 256 out_str += format_string.format(GetProcPID(proc_val), proc_val.p_memstat_effectivepriority, 257 proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_relaunch_flags, 258 proc_val.p_memstat_userdata, phys_mem_footprint, iokit_footprint, phys_footprint) 259 if phys_footprint != phys_footprint_spike: 260 out_str += "{: >12d}".format(phys_footprint_spike) 261 else: 262 out_str += "{: >12s}".format('-') 263 264 out_str += "{: >10d} ".format(phys_footprint_lifetime_max) 265 out_str += "{: >10d} {: <32s}\n".format(phys_footprint_limit, GetProcName(proc_val)) 266 return out_str 267 268@lldb_command('showmemorystatus') 269def ShowMemoryStatus(cmd_args=None): 270 """ Routine to display each entry in jetsam list with a summary of pressure statistics 271 Usage: showmemorystatus 272 """ 273 bucket_index = 0 274 bucket_count = 20 275 print GetMemoryStatusNode.header 276 print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)", 277 "(pages)", "(pages)", "(pages)") 278 while bucket_index < bucket_count: 279 current_bucket = kern.globals.memstat_bucket[bucket_index] 280 current_list = current_bucket.list 281 current_proc = Cast(current_list.tqh_first, 'proc *') 282 while unsigned(current_proc) != 0: 283 print GetMemoryStatusNode(current_proc) 284 current_proc = current_proc.p_memstat_list.tqe_next 285 bucket_index += 1 286 print "\n\n" 287 Memstats() 288 289# EndMacro: showmemorystatus 290 291class ZoneMeta(object): 292 """ 293 Helper class that helpers walking metadata 294 """ 295 296 def __init__(self, addr, isPageIndex = False): 297 global kern 298 pagesize = kern.globals.page_size 299 zone_info = kern.GetGlobalVariable('zone_info') 300 301 def load_range(var): 302 return (unsigned(var.min_address), unsigned(var.max_address)) 303 304 def in_range(x, r): 305 return x >= r[0] and x <= r[1] 306 307 FOREIGN = GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_FOREIGN') 308 NATIVE = GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_NATIVE') 309 310 self.meta_range = load_range(zone_info.zi_meta_range) 311 self.native_range = load_range(zone_info.zi_map_range[NATIVE]) 312 self.foreign_range = load_range(zone_info.zi_map_range[FOREIGN]) 313 self.addr_base = min(self.foreign_range[0], self.native_range[0]) 314 315 addr = unsigned(addr) 316 if isPageIndex: 317 # sign extend 318 addr = value(pagesize.GetSBValue().CreateValueFromExpression(None, 319 '(long)(int)%d * %d' %(addr, pagesize))) 320 addr = unsigned(addr) 321 322 self.address = addr 323 324 if in_range(addr, self.meta_range): 325 self.kind = 'Metadata' 326 addr -= addr % sizeof('struct zone_page_metadata') 327 self.meta_addr = addr 328 self.meta = kern.GetValueFromAddress(addr, "struct zone_page_metadata *") 329 330 self.page_addr = self.addr_base + ((addr - self.meta_range[0]) / sizeof('struct zone_page_metadata') * pagesize) 331 elif in_range(addr, self.native_range) or in_range(addr, self.foreign_range): 332 addr &= ~(pagesize - 1) 333 page_idx = (addr - self.addr_base) / pagesize 334 335 self.kind = 'Element' 336 self.page_addr = addr 337 self.meta_addr = self.meta_range[0] + page_idx * sizeof('struct zone_page_metadata') 338 self.meta = kern.GetValueFromAddress(self.meta_addr, "struct zone_page_metadata *") 339 else: 340 self.kind = 'Unknown' 341 self.meta = None 342 self.page_addr = 0 343 self.meta_addr = 0 344 345 if self.meta: 346 self.zone = addressof(kern.globals.zone_array[self.meta.zm_index]) 347 self.zone_security = addressof(kern.globals.zone_security_array[self.meta.zm_index]) 348 else: 349 self.zone = None 350 self.zone_security = None 351 352 def isSecondaryPage(self): 353 return self.meta and self.meta.zm_chunk_len >= 0xe 354 355 def getPageCount(self): 356 n = self.meta and self.meta.zm_chunk_len or 0 357 if self.zone and self.zone.z_percpu: 358 n *= kern.globals.zpercpu_early_count 359 return n 360 361 def getAllocAvail(self): 362 if not self.meta: return 0 363 chunk_len = unsigned(self.meta.zm_chunk_len) 364 page_size = unsigned(kern.globals.page_size) 365 return chunk_len * page_size / self.zone.z_elem_size 366 367 def getAllocCount(self): 368 if not self.meta: return 0 369 return self.meta.zm_alloc_size / self.zone.z_elem_size 370 371 def getReal(self): 372 if self.isSecondaryPage(): 373 return ZoneMeta(unsigned(self.meta) - sizeof('struct zone_page_metadata') * unsigned(self.meta.zm_page_index)) 374 375 return self 376 377 def getElementAddress(self, addr): 378 meta = self.getReal() 379 esize = meta.zone.z_elem_size 380 start = meta.page_addr 381 382 if esize == 0: 383 return None 384 385 estart = addr - start 386 return unsigned(start + estart - (estart % esize)) 387 388 def getInlineBitmapChunkLength(self): 389 if self.zone.z_percpu: 390 return unsigned(self.zone.z_chunk_pages) 391 return unsigned(self.meta.zm_chunk_len) 392 393 def getBitmapSize(self): 394 if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len: 395 return 0 396 if self.meta.zm_inline_bitmap: 397 return -4 * self.getInlineBitmapChunkLength() 398 return 8 << (unsigned(self.meta.zm_bitmap) & 0x7); 399 400 def getBitmap(self): 401 if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len: 402 return 0 403 if self.meta.zm_inline_bitmap: 404 return unsigned(addressof(self.meta.zm_bitmap)) 405 bbase = unsigned(kern.globals.zone_info.zi_bits_range.min_address) 406 index = unsigned(self.meta.zm_bitmap) & ~0x7 407 return bbase + index; 408 409 def getFreeCountSlow(self): 410 if not self.meta or self.zone.z_permanent or not self.meta.zm_chunk_len: 411 return self.getAllocAvail() - self.getAllocCount() 412 413 n = 0 414 if self.meta.zm_inline_bitmap: 415 for i in xrange(0, self.getInlineBitmapChunkLength()): 416 m = kern.GetValueFromAddress(self.meta_addr + i * 16, 417 'struct zone_page_metadata *'); 418 bits = unsigned(m.zm_bitmap) 419 while bits: 420 n += 1 421 bits &= bits - 1 422 else: 423 bitmap = kern.GetValueFromAddress(self.getBitmap(), 'uint64_t *') 424 for i in xrange(0, 1 << (unsigned(self.meta.zm_bitmap) & 0x7)): 425 bits = unsigned(bitmap[i]) 426 while bits: 427 n += 1 428 bits &= bits - 1 429 return n 430 431 def isElementFree(self, addr): 432 meta = self.meta 433 434 if not meta or self.zone.z_permanent or not meta.zm_chunk_len: 435 return True 436 437 start = self.page_addr 438 esize = self.zone.z_elem_size 439 eidx = (addr - start) / esize 440 441 if meta.zm_inline_bitmap: 442 i = eidx / 32 443 m = unsigned(meta) + sizeof('struct zone_page_metadata') * i 444 bits = kern.GetValueFromAddress(m, meta).zm_bitmap 445 return (bits & (1 << (eidx % 32))) != 0 446 447 else: 448 bitmap = kern.GetValueFromAddress(self.getBitmap(), 'uint64_t *') 449 bits = unsigned(bitmap[eidx / 64]) 450 return (bits & (1 << (eidx % 64))) != 0 451 452 def iterateElements(self): 453 if self.meta is None: 454 return 455 esize = self.zone.z_elem_size 456 start = 0 457 end = unsigned(kern.globals.page_size) * self.meta.zm_chunk_len 458 end -= end % esize 459 460 for offs in xrange(start, end, esize): 461 yield unsigned(self.page_addr + offs) 462 463@lldb_type_summary(['zone_page_metadata']) 464@header("{:<20s} {:<10s} {:<10s} {:<24s} {:<20s} {:<20s}".format( 465 'METADATA', 'PG_CNT', 'ALLOC_CNT', 'BITMAP', 'ZONE', 'NAME')) 466def GetZoneMetadataSummary(meta): 467 """ Summarize a zone metadata object 468 params: meta - obj representing zone metadata in the kernel 469 returns: str - summary of the zone metadata 470 """ 471 472 if type(meta) != ZoneMeta: 473 meta = ZoneMeta(meta) 474 475 out_str = 'Metadata Description:\n' + GetZoneMetadataSummary.header + '\n' 476 if meta.isSecondaryPage(): 477 out_str += "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}\n".format( 478 meta.meta_addr, 0, 0, 0, 0, 0, '(fake multipage meta)') 479 meta = meta.getReal() 480 out_str += "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}".format( 481 meta.meta_addr, meta.getPageCount(), meta.getAllocCount(), 482 meta.getBitmap(), meta.getBitmapSize(), meta.zone, 483 ZoneName(meta.zone, meta.zone_security)) 484 return out_str 485 486@header("{:<20s} {:<10s} {:<10s} {:<20s} {:<10s}".format( 487 'ADDRESS', 'TYPE', 'STATUS', 'PAGE_ADDR', 'OFFSET')) 488def WhatIs(addr): 489 """ Information about kernel pointer 490 """ 491 global kern 492 493 meta = ZoneMeta(addr) 494 estart = None 495 496 if meta.meta is None: 497 out_str = "Address {:#018x} is outside of any zone map ({:#018x}-{:#018x})\n".format( 498 addr, meta.native_range[0], meta.native_range[-1] + 1) 499 else: 500 if meta.kind[0] == 'E': # element 501 page_offset_str = "{:d}/{:d}K".format( 502 addr - meta.page_addr, kern.globals.page_size / 1024) 503 estart = meta.getElementAddress(addr) 504 if estart is None: 505 status = "Unattributed" 506 elif meta.isElementFree(estart): 507 status = "Free" 508 elif estart in GetZoneCachedElements(meta.zone): 509 status = "Cached" 510 else: 511 status = "Allocated" 512 else: 513 page_offset_str = "-" 514 status = "-" 515 out_str = WhatIs.header + '\n' 516 out_str += "{meta.address:<#20x} {meta.kind:<10s} {status:<10s} {meta.page_addr:<#20x} {:<10s}\n\n".format( 517 page_offset_str, meta=meta, status=status) 518 out_str += GetZoneMetadataSummary(meta) + '\n\n' 519 520 print out_str 521 522 if estart is not None: 523 print "Hexdump:\n" 524 525 meta = meta.getReal() 526 esize = meta.zone.z_elem_size 527 start = meta.page_addr 528 marks = {unsigned(addr): ">"} 529 530 try: 531 if estart > start: 532 data_array = kern.GetValueFromAddress(estart - 16, "uint8_t *") 533 print_hex_data(data_array[0:16], estart - 16, "") 534 except: 535 pass 536 537 print "------------------------------------------------------------------" 538 try: 539 data_array = kern.GetValueFromAddress(estart, "uint8_t *") 540 print_hex_data(data_array[0:esize], estart, "", marks) 541 except: 542 print "*** unable to read memory ***" 543 pass 544 print "------------------------------------------------------------------" 545 546 try: 547 data_array = kern.GetValueFromAddress(estart + esize, "uint8_t *") 548 print_hex_data(data_array[0:16], estart + esize, "") 549 except: 550 pass 551 552@lldb_command('whatis') 553def WhatIsHelper(cmd_args=None): 554 """ Routine to show information about a kernel pointer 555 Usage: whatis <address> 556 """ 557 if not cmd_args: 558 raise ArgumentError("No arguments passed") 559 WhatIs(kern.GetValueFromAddress(cmd_args[0], 'void *')) 560 561# Macro: showzcache 562 563@lldb_type_summary(['zone','zone_t']) 564@header("{:18s} {:32s} {:>6s} {:>6s} {:>6s} {:>6s} {:>6s} {:>6s} {:<s}".format( 565 'ZONE', 'NAME', 'WSS', 'CONT', 'USED', 'FREE', 'CACHED', 'RECIRC', 'CPU_CACHES')) 566def GetZoneCacheCPUSummary(zone, zone_security, verbose, O): 567 """ Summarize a zone's cache broken up per cpu 568 params: 569 zone: value - obj representing a zone in kernel 570 returns: 571 str - summary of the zone's per CPU cache contents 572 """ 573 format_string = '{zone:#018x} {:32s} ' 574 format_string += '{zone.z_elems_free_wss:6d} {cont:6.2f} ' 575 format_string += '{used:6d} {zone.z_elems_free:6d} ' 576 format_string += '{cached:6d} {recirc:6d} {cpuinfo:s}' 577 cache_elem_count = 0 578 cpu_info = "" 579 mag_capacity = unsigned(kern.GetGlobalVariable('zc_magazine_size')) 580 depot_capacity = kern.GetGlobalVariable('depot_element_count') 581 582 if zone.z_pcpu_cache: 583 if verbose: 584 cpu_info = None 585 for cache in IterateZPerCPU(zone.z_pcpu_cache): 586 if cpu_info is None: 587 cpu_info = "{ " 588 else: 589 cpu_info += ", " 590 per_cpu_count = unsigned(cache.zc_alloc_cur) 591 per_cpu_count += unsigned(cache.zc_free_cur) 592 per_cpu_count += unsigned(cache.zc_depot_cur) * mag_capacity 593 cache_elem_count += per_cpu_count 594 cpu_info += "{:3d} /{cache.zc_depot_max:3d}".format(per_cpu_count, cache=cache) 595 cpu_info += " }" 596 else: 597 depot_cur = 0 598 depot_max = 0 599 for cache in IterateZPerCPU(zone.z_pcpu_cache): 600 depot_cur += unsigned(cache.zc_alloc_cur) 601 depot_cur += unsigned(cache.zc_free_cur) 602 cache_elem_count += unsigned(cache.zc_depot_cur) * mag_capacity 603 depot_max += unsigned(cache.zc_depot_max) 604 cache_elem_count += depot_cur 605 606 cpus = unsigned(kern.globals.zpercpu_early_count) 607 cpu_info = "total: {:3d} / {:3d}, avg: {:5.1f} / {:5.1f}".format( 608 depot_cur, depot_max, float(depot_cur) / cpus, float(depot_max) / cpus) 609 610 611 print O.format(format_string, ZoneName(zone, zone_security), 612 cached=cache_elem_count, 613 used=zone.z_elems_avail - cache_elem_count - zone.z_elems_free, 614 cont=float(zone.z_contention_wma) / 256., 615 recirc=zone.z_recirc_cur * mag_capacity, 616 zone=zone, cpuinfo = cpu_info) 617 618@lldb_command('showzcache', fancy=True) 619def ZcacheCPUPrint(cmd_args=None, cmd_options={}, O=None): 620 """ 621 Routine to print a summary listing of all the kernel zones cache contents 622 623 Usage: showzcache [-V] 624 625 Use -V to see more detailed output 626 """ 627 global kern 628 verbose = "-V" in cmd_options 629 with O.table(GetZoneCacheCPUSummary.header): 630 if len(cmd_args) == 1: 631 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *') 632 zone_array = [z[0] for z in kern.zones] 633 zid = zone_array.index(zone) 634 zone_security = kern.zones[zid][1] 635 GetZoneCacheCPUSummary(zone, zone_security, verbose, O); 636 else: 637 for zval, zsval in kern.zones: 638 if zval.z_self: 639 GetZoneCacheCPUSummary(zval, zsval, verbose, O) 640 641# EndMacro: showzcache 642 643# Macro: zprint 644 645def GetZone(zone_val, zs_val, marks, security_marks): 646 """ Internal function which gets a phython dictionary containing important zone information. 647 params: 648 zone_val: value - obj representing a zone in kernel 649 returns: 650 zone - python dictionary with zone stats 651 """ 652 pcpu_scale = 1 653 if zone_val.z_percpu: 654 pcpu_scale = unsigned(kern.globals.zpercpu_early_count) 655 pagesize = kern.globals.page_size 656 zone = {} 657 mag_capacity = unsigned(kern.GetGlobalVariable('zc_magazine_size')) 658 zone["page_count"] = unsigned(zone_val.z_wired_cur) * pcpu_scale 659 zone["allfree_page_count"] = unsigned(zone_val.z_wired_empty) 660 661 cache_elem_count = 0 662 if zone_val.z_pcpu_cache: 663 for cache in IterateZPerCPU(zone_val.z_pcpu_cache): 664 cache_elem_count += unsigned(cache.zc_alloc_cur) 665 cache_elem_count += unsigned(cache.zc_free_cur) 666 cache_elem_count += unsigned(cache.zc_depot_cur) * mag_capacity 667 668 zone["size"] = zone["page_count"] * pagesize 669 zone["submap_idx"] = unsigned(zs_val.z_submap_idx) 670 671 zone["free_size"] = zone_val.z_elems_free * zone_val.z_elem_size * pcpu_scale 672 zone["cached_size"] = cache_elem_count * zone_val.z_elem_size * pcpu_scale 673 zone["used_size"] = zone["size"] - zone["free_size"] - zone["cached_size"] 674 675 zone["element_count"] = zone_val.z_elems_avail - zone_val.z_elems_free - cache_elem_count 676 zone["cache_element_count"] = cache_elem_count 677 zone["free_element_count"] = unsigned(zone_val.z_elems_free) 678 679 if zone_val.z_percpu: 680 zone["allocation_size"] = unsigned(pagesize) 681 zone["allocation_ncpu"] = unsigned(zone_val.z_chunk_pages) 682 else: 683 zone["allocation_size"] = unsigned(zone_val.z_chunk_pages * pagesize) 684 zone["allocation_ncpu"] = 1 685 zone["allocation_count"] = zone["allocation_size"] / zone_val.z_elem_size 686 zone["allocation_waste"] = (zone["allocation_size"] % zone_val.z_elem_size) * zone["allocation_ncpu"] 687 688 if not zone_val.__getattr__("z_self") : 689 zone["destroyed"] = True 690 else: 691 zone["destroyed"] = False 692 693 for mark in marks: 694 if zone_val.__getattr__(mark[0]): 695 zone[mark[0]] = True 696 else: 697 zone[mark[0]] = False 698 699 for mark in security_marks: 700 if zs_val.__getattr__(mark[0]): 701 zone[mark[0]] = True 702 else: 703 zone[mark[0]] = False 704 705 zone["name"] = ZoneName(zone_val, zs_val) 706 if zone_val.exhaustible: 707 zone["exhaustible"] = True 708 else: 709 zone["exhaustible"] = False 710 711 zone["sequester_page_count"] = (unsigned(zone_val.z_va_cur) - 712 unsigned(zone_val.z_wired_cur)) * pcpu_scale 713 zone["page_count_max"] = unsigned(zone_val.z_wired_max) * pcpu_scale 714 715 # Ensure the zone is serializable 716 json.dumps(zone) 717 return zone 718 719 720@lldb_type_summary(['zone','zone_t']) 721@header(("{:<18s} {:_^47s} {:_^24s} {:_^13s} {:_^28s}\n"+ 722"{:<18s} {:>11s} {:>11s} {:>11s} {:>11s} {:>8s} {:>7s} {:>7s} {:>6s} {:>6s} {:>8s} {:>6s} {:>5s} {:>7s} {:<22s} {:<20s}").format( 723'', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG', 724'ZONE', 'TOTAL', 'ALLOC', 'CACHE', 'FREE', 'ALLOC', 'CACHE', 'FREE', 'COUNT', 'FREE', 'SIZE (P)', 'ELTS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME')) 725def GetZoneSummary(zone_val, zs_val, marks, security_marks, stats): 726 """ Summarize a zone with important information. See help zprint for description of each field 727 params: 728 zone_val: value - obj representing a zone in kernel 729 returns: 730 str - summary of the zone 731 """ 732 pagesize = kern.globals.page_size 733 out_string = "" 734 zone = GetZone(zone_val, zs_val, marks, security_marks) 735 736 pcpu_scale = 1 737 if zone_val.z_percpu: 738 pcpu_scale = unsigned(kern.globals.zpercpu_early_count) 739 740 format_string = '{zone:#018x} {zd[size]:11,d} {zd[used_size]:11,d} {zd[cached_size]:11,d} {zd[free_size]:11,d} ' 741 format_string += '{zd[element_count]:8,d} {zd[cache_element_count]:7,d} {zone.z_elems_free:7,d} ' 742 format_string += '{z_wired_cur:6,d} {z_wired_empty:6,d} ' 743 format_string += '{alloc_size_kb:3,d}K ({zone.z_chunk_pages:d}) ' 744 format_string += '{zd[allocation_count]:6,d} {zd[allocation_waste]:5,d} {z_elem_size:7,d} ' 745 format_string += '{markings:<22s} {zone_name:<20s}' 746 747 markings="" 748 if zone["destroyed"]: 749 markings+="I" 750 else: 751 markings+=" " 752 753 for mark in marks: 754 if zone[mark[0]]: 755 markings += mark[1] 756 else: 757 markings+=" " 758 for mark in security_marks: 759 if zone[mark[0]]: 760 markings += mark[1] 761 else: 762 markings+=" " 763 764 765 """ Z_SUBMAP_IDX_READ_ONLY == 1 766 """ 767 if zone["submap_idx"] == 1: 768 markings += "%" 769 else: 770 markings+=" " 771 772 alloc_size_kb = zone["allocation_size"] / 1024 773 out_string += format_string.format(zone=zone_val, zd=zone, 774 z_wired_cur=unsigned(zone_val.z_wired_cur) * pcpu_scale, 775 z_wired_empty=unsigned(zone_val.z_wired_empty) * pcpu_scale, 776 z_elem_size=unsigned(zone_val.z_elem_size) * pcpu_scale, 777 alloc_size_kb=alloc_size_kb, markings=markings, zone_name=zone["name"]) 778 779 if zone["exhaustible"] : 780 out_string += " (max: {:d})".format(zone["page_count_max"] * pagesize) 781 782 if zone["sequester_page_count"] != 0 : 783 out_string += " (sequester: {:d})".format(zone["sequester_page_count"]) 784 785 stats["cur_size"] += zone["size"] 786 stats["used_size"] += zone["used_size"] 787 stats["cached_size"] += zone["cached_size"] 788 stats["free_size"] += zone["free_size"] 789 stats["cur_pages"] += zone["page_count"] 790 stats["free_pages"] += zone["allfree_page_count"] 791 stats["seq_pages"] += zone["sequester_page_count"] 792 793 return out_string 794 795@lldb_command('zprint', "J", fancy=True) 796def Zprint(cmd_args=None, cmd_options={}, O=None): 797 """ Routine to print a summary listing of all the kernel zones 798 usage: zprint -J 799 Output json 800 All columns are printed in decimal 801 Legend: 802 ! - zone uses VA sequestering 803 $ - not encrypted during hibernation 804 % - zone is a read-only zone 805 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv 806 C - collectable 807 D - destructible 808 E - Per-cpu caching is enabled for this zone 809 F - allows foreign memory (memory not allocated from any zone map) 810 G - currently running GC 811 H - exhaustible 812 I - zone was destroyed and is no longer valid 813 L - zone is being monitored by zleaks 814 M - gzalloc will avoid monitoring this zone 815 N - zone requires alignment (avoids padding this zone for debugging) 816 O - does not allow refill callout to fill zone on noblock allocation 817 R - will be refilled when below low water mark 818 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv 819 X - expandable 820 """ 821 global kern 822 823 marks = [ 824 ["collectable", "C"], 825 ["z_destructible", "D"], 826 ["expandable", "X"], 827 ["exhaustible", "H"], 828 ["z_elems_rsv", "R"], 829 ["no_callout", "O"], 830 ["zleak_on", "L"], 831 ["z_expander", "A"], 832 ["z_expander_vm_priv", "S"], 833 ["z_pcpu_cache", "E"], 834 ["gzalloc_exempt", "M"], 835 ["alignment_required", "N"], 836 ] 837 security_marks = [ 838 ["z_allows_foreign", "F"], 839 ["z_va_sequester", "!"], 840 ["z_noencrypt", "$"], 841 ] 842 843 stats = { 844 "cur_size": 0, "used_size": 0, "cached_size": 0, "free_size": 0, 845 "cur_pages": 0, "free_pages": 0, "seq_pages": 0 846 } 847 848 print_json = False 849 if "-J" in cmd_options: 850 print_json = True 851 852 if print_json: 853 zones = [] 854 for zval, zsval in kern.zones: 855 if zval.z_self: 856 zones.append(GetZone(zval, zsval, marks, security_marks)) 857 858 print json.dumps(zones) 859 else: 860 with O.table(GetZoneSummary.header): 861 for zval, zsval in kern.zones: 862 if zval.z_self: 863 print GetZoneSummary(zval, zsval, marks, security_marks, stats) 864 865 format_string = '{VT.Bold}{name:19s} {stats[cur_size]:11,d} {stats[used_size]:11,d} {stats[cached_size]:11,d} {stats[free_size]:11,d} ' 866 format_string += ' ' 867 format_string += '{stats[cur_pages]:6,d} {stats[free_pages]:6,d}{VT.EndBold} ' 868 format_string += '(sequester: {VT.Bold}{stats[seq_pages]:,d}{VT.EndBold})' 869 print O.format(format_string, name="TOTALS", filler="", stats=stats) 870 871 872@xnudebug_test('test_zprint') 873def TestZprint(kernel_target, config, lldb_obj, isConnected ): 874 """ Test the functionality of zprint command 875 returns 876 - False on failure 877 - True on success 878 """ 879 if not isConnected: 880 print "Target is not connected. Cannot test memstats" 881 return False 882 res = lldb.SBCommandReturnObject() 883 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res) 884 result = res.GetOutput() 885 if len(result.split("\n")) > 2: 886 return True 887 else: 888 return False 889 890 891# EndMacro: zprint 892# Macro: showtypes 893def PrintTypes(z): 894 kt_cur = cast(z.z_views, "struct kalloc_type_view *") 895 prev_types = {} 896 print ' {0: <24s} {1: <40s} {2: <10s}'.format( 897 "kalloc_type_view", "typename", "signature") 898 while kt_cur: 899 typename = str(kt_cur.kt_zv.zv_name) 900 if "site." in typename: 901 typename = typename.split("site.")[1] 902 sig = str(kt_cur.kt_signature) 903 if typename not in prev_types or prev_types[typename] != sig: 904 print ' {0: <#24x} {1: <40s} {2: <10s}'.format( 905 kt_cur, typename, sig) 906 prev_types[typename] = sig 907 kt_cur = cast(kt_cur.kt_zv.zv_next, "struct kalloc_type_view *") 908 909def ShowTypesPerSize(size): 910 kalloc_type_zarray = kern.GetGlobalVariable('kalloc_type_zarray') 911 num_kt_sizeclass = kern.GetGlobalVariable('num_kt_sizeclass') 912 for i in range(num_kt_sizeclass): 913 zone = kalloc_type_zarray[i] 914 if zone and zone.z_elem_size == size: 915 while zone: 916 print "Zone: %s (0x%x)" % (zone.z_name, zone) 917 PrintTypes(zone) 918 zone = zone.z_kt_next 919 break 920 921def ShowAllTypes(): 922 kalloc_type_zarray = kern.GetGlobalVariable('kalloc_type_zarray') 923 num_kt_sizeclass = kern.GetGlobalVariable('num_kt_sizeclass') 924 for i in range(num_kt_sizeclass): 925 zone = kalloc_type_zarray[i] 926 while zone: 927 print "Zone: %s (0x%x)" % (zone.z_name, zone) 928 PrintTypes(zone) 929 zone = zone.z_kt_next 930 931@lldb_command('showkalloctypes', 'Z:S:') 932def ShowKallocTypes(cmd_args=None, cmd_options={}): 933 """ 934 prints kalloc types for a zone or sizeclass 935 936 Usage: showkalloctypes [-Z <zone>] [-S <sizeclass>] 937 938 If no options are provided kalloc types for all zones is printed 939 """ 940 if '-Z' in cmd_options: 941 zone = kern.GetValueFromAddress(cmd_options['-Z'], 'struct zone *') 942 if not zone: 943 raise ArgumentError("Invalid zone {:s}".format(cmd_options['-Z'])) 944 PrintTypes(zone) 945 return 946 if '-S' in cmd_options: 947 size = unsigned(cmd_options['-S']) 948 if size == 0: 949 raise ArgumentError("Invalid size {:s}".format(cmd_options['-S'])) 950 ShowTypesPerSize(size) 951 return 952 ShowAllTypes() 953 954# EndMacro: showkalloctypes 955# Macro: showzchunks 956 957def ZoneIteratePageQueue(page): 958 while page.packed_address: 959 meta = ZoneMeta(page.packed_address, isPageIndex=True) 960 yield meta 961 page = meta.meta.zm_page_next 962 963@header("{: <20s} {: <20s} {: <20s} {: <25s} {: <10s} {: <8s} {: <4s} {: >9s}".format( 964 "Zone", "Metadata", "Page", "Bitmap", "Kind", "Queue", "Pgs", "Allocs")) 965def GetZoneChunk(meta, queue, O=None): 966 format_string = "{meta.zone: <#20x} " 967 format_string += "{meta.meta_addr: <#20x} {meta.page_addr: <#20x} " 968 format_string += "{bitmap: <#18x} @{bitmap_size:<5d} " 969 format_string += "{kind:<10s} {queue:<8s} {pgs:<1d}/{chunk:<1d} " 970 format_string += "{alloc_count: >4d}/{avail_count: >4d}" 971 972 pgs = int(meta.zone.z_chunk_pages) 973 chunk = pgs 974 if meta.meta.zm_chunk_len >= 0xe: 975 kind = "secondary" 976 pgs -= int(meta.meta.zm_page_index) 977 else: 978 kind = "primary" 979 980 alloc_count=meta.getAllocCount() 981 avail_count=meta.getAllocAvail() 982 free_count=meta.getFreeCountSlow() 983 984 if alloc_count + free_count != avail_count: 985 format_string += " {VT.Red}bitmap mismatch{VT.Default}" 986 987 return O.format(format_string, meta=meta, 988 alloc_count=alloc_count, 989 avail_count=avail_count, 990 bitmap=meta.getBitmap(), 991 bitmap_size=meta.getBitmapSize(), 992 queue=queue, kind=kind, pgs=pgs, chunk=chunk) 993 994def ShowZChunksImpl(zone, extra_addr=None, cmd_options={}, O=None): 995 verbose = '-V' in cmd_options 996 cached = GetZoneCachedElements(zone) 997 998 def do_content(meta, O, indent=False): 999 with O.table("{:>5s} {:<20s} {:<10s}".format("#", "Element", "State"), indent=indent): 1000 i = 0 1001 for e in meta.iterateElements(): 1002 status = "Allocated" 1003 if meta.isElementFree(e): 1004 status = "Free" 1005 elif e in cached: 1006 status = "Cached" 1007 print O.format("{:5d} {:<#20x} {:10s}", i, e, status) 1008 i += 1 1009 1010 if extra_addr is None: 1011 with O.table(GetZoneChunk.header): 1012 for meta in ZoneIteratePageQueue(zone.z_pageq_full): 1013 print GetZoneChunk(meta, "full", O) 1014 if verbose: do_content(meta, O, indent=True); 1015 1016 for meta in ZoneIteratePageQueue(zone.z_pageq_partial): 1017 print GetZoneChunk(meta, "partial", O) 1018 if verbose: do_content(meta, O, indent=True); 1019 1020 for meta in ZoneIteratePageQueue(zone.z_pageq_empty): 1021 print GetZoneChunk(meta, "empty", O) 1022 if verbose: do_content(meta, O, indent=True); 1023 1024 for meta in ZoneIteratePageQueue(zone.z_pageq_va): 1025 print GetZoneChunk(meta, "va", O) 1026 else: 1027 meta = ZoneMeta(extra_addr, isPageIndex="-I" in cmd_options).getReal() 1028 with O.table(GetZoneChunk.header): 1029 print GetZoneChunk(meta, "N/A", O) 1030 do_content(meta, O) 1031 1032@lldb_command('showzchunks', "IV", fancy=True) 1033def ShowZChunks(cmd_args=None, cmd_options={}, O=None): 1034 """ 1035 prints the list of zone chunks, or the content of a given chunk 1036 1037 Usage: showzchunks <zone> [-I] [-V] [address] 1038 1039 Use -I to interpret [address] as a page index 1040 Use -V to show the contents of all the chunks 1041 1042 [address] can by any address belonging to the zone, or metadata 1043 """ 1044 1045 if not cmd_args: 1046 return O.error('missing zone argument') 1047 1048 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *') 1049 1050 if len(cmd_args) == 1: 1051 ShowZChunksImpl(zone, cmd_options=cmd_options, O=O) 1052 else: 1053 addr = unsigned(kern.GetValueFromAddress(cmd_args[1])) 1054 ShowZChunksImpl(zone, extra_addr=addr, cmd_options=cmd_options, O=O) 1055 1056@lldb_command('showallzchunks', fancy=True) 1057def ShowAllZChunks(cmd_args=None, cmd_options={}, O=None): 1058 """ 1059 prints the list of all zone chunks 1060 1061 Usage: showallzchunks 1062 """ 1063 1064 for z, zs in kern.zones: 1065 ShowZChunksImpl(z, O=O) 1066 1067# EndMacro: showzchunks 1068# Macro: zstack_showzonesbeinglogged 1069 1070@lldb_command('zstack_showzonesbeinglogged') 1071def ZstackShowZonesBeingLogged(cmd_args=None): 1072 """ Show all zones which have BTLog enabled. 1073 """ 1074 global kern 1075 for zval, zsval in kern.zones: 1076 if zval.zlog_btlog: 1077 print "Zone: %s with its BTLog at: 0x%lx" % (ZoneName(zval, zsval), 1078 zval.zlog_btlog) 1079 1080# EndMacro: zstack_showzonesbeinglogged 1081 1082# Macro: zstack 1083 1084@lldb_command('zstack') 1085def Zstack(cmd_args=None): 1086 """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>. 1087 Usage: zstack <btlog addr> <index> [<count>] 1088 1089 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%). 1090 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that. 1091 """ 1092 if not cmd_args: 1093 print Zstack.__doc__ 1094 return 1095 if int(kern.globals.log_records) == 0: 1096 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args." 1097 return 1098 1099 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 1100 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize) 1101 btrecord_size = unsigned(btlog_ptr.btrecord_size) 1102 btrecords = unsigned(btlog_ptr.btrecords) 1103 btlog_size = unsigned(sizeof('struct btlog')) 1104 depth = unsigned(btlog_ptr.btrecord_btdepth) 1105 zstack_index = ArgumentStringToInt(cmd_args[1]) 1106 count = 1 1107 if len(cmd_args) >= 3: 1108 count = ArgumentStringToInt(cmd_args[2]) 1109 1110 max_count = ((btrecords_total_size - btlog_size)/btrecord_size) 1111 1112 if (zstack_index + count) > max_count: 1113 count = max_count - zstack_index 1114 1115 while count and (zstack_index != 0xffffff): 1116 zstack_record_offset = zstack_index * btrecord_size 1117 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *') 1118 if int(zstack_record.ref_count)!=0: 1119 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count)) 1120 zstack_index += 1 1121 count -= 1 1122 1123# EndMacro : zstack 1124 1125# Macro: zstack_inorder 1126 1127@lldb_command('zstack_inorder') 1128def ZstackInOrder(cmd_args=None): 1129 """ Zone leak debugging: Print the stack traces starting from head to the tail. 1130 Usage: zstack_inorder <btlog addr> 1131 """ 1132 if not cmd_args: 1133 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>" 1134 return 1135 if int(kern.globals.log_records) == 0: 1136 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args." 1137 return 1138 1139 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 1140 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize) 1141 btrecord_size = unsigned(btlog_ptr.btrecord_size) 1142 btrecords = unsigned(btlog_ptr.btrecords) 1143 btlog_size = unsigned(sizeof('struct btlog')) 1144 depth = unsigned(btlog_ptr.btrecord_btdepth) 1145 zstack_head = unsigned(btlog_ptr.head) 1146 zstack_index = zstack_head 1147 zstack_tail = unsigned(btlog_ptr.tail) 1148 count = ((btrecords_total_size - btlog_size)/btrecord_size) 1149 1150 while count and (zstack_index != 0xffffff): 1151 zstack_record_offset = zstack_index * btrecord_size 1152 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *') 1153 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count)) 1154 zstack_index = zstack_record.next 1155 count -= 1 1156 1157# EndMacro : zstack_inorder 1158 1159# Macro: findoldest 1160 1161@lldb_command('findoldest') 1162def FindOldest(cmd_args=None): 1163 """ 1164 """ 1165 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead." 1166 return 1167# EndMacro : findoldest 1168 1169# Macro : zstack_findleak 1170 1171@lldb_command('zstack_findleak') 1172def zstack_findleak(cmd_args=None): 1173 """ Zone leak debugging: search the log and print the stack with the most active references 1174 in the stack trace. 1175 Usage: zstack_findleak <btlog address> 1176 1177 This is useful for verifying a suspected stack as being the source of 1178 the leak. 1179 """ 1180 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 1181 btrecord_size = unsigned(btlog_ptr.btrecord_size) 1182 btrecords = unsigned(btlog_ptr.btrecords) 1183 1184 cpcs_index = unsigned(btlog_ptr.head) 1185 depth = unsigned(btlog_ptr.btrecord_btdepth) 1186 highref = 0 1187 highref_index = 0 1188 highref_record = 0 1189 1190 while cpcs_index != 0xffffff: 1191 cpcs_record_offset = cpcs_index * btrecord_size 1192 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *') 1193 if cpcs_record.ref_count > highref: 1194 highref_record = cpcs_record 1195 highref = cpcs_record.ref_count 1196 highref_index = cpcs_index 1197 cpcs_index = cpcs_record.next 1198 ShowZStackRecord(highref_record, highref_index, depth, unsigned(btlog_ptr.active_element_count)) 1199 1200# EndMacro: zstack_findleak 1201 1202# Macro: findelem 1203 1204@lldb_command('findelem') 1205def FindElem(cmd_args=None): 1206 """ 1207 """ 1208 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead." 1209 return 1210# EndMacro: findelem 1211 1212@lldb_command('zstack_findelem') 1213def ZStackFindElem(cmd_args=None): 1214 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that 1215 refer to the given zone element. 1216 Usage: zstack_findelem <btlog addr> <elem addr> 1217 1218 When the kernel panics due to a corrupted zone element, get the 1219 element address and use this command. This will show you the stack traces of all logged zalloc and 1220 zfree operations which tells you who touched the element in the recent past. This also makes 1221 double-frees readily apparent. 1222 """ 1223 if not cmd_args: 1224 print ZStackFindElem.__doc__ 1225 return 1226 if int(kern.globals.log_records) == 0 or unsigned(kern.globals.corruption_debug_flag) == 0: 1227 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args." 1228 return 1229 1230 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 1231 target_element = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *')) 1232 1233 btrecord_size = unsigned(btlog_ptr.btrecord_size) 1234 btrecords = unsigned(btlog_ptr.btrecords) 1235 depth = unsigned(btlog_ptr.btrecord_btdepth) 1236 1237 prev_op = -1 1238 scan_items = 0 1239 hashelem = cast(btlog_ptr.elem_linkage_un.element_hash_queue.tqh_first, 'btlog_element_t *') 1240 if (target_element >> 32) != 0: 1241 target_element = target_element ^ 0xFFFFFFFFFFFFFFFF 1242 else: 1243 target_element = target_element ^ 0xFFFFFFFF 1244 while hashelem != 0: 1245 if unsigned(hashelem.elem) == target_element: 1246 recindex = hashelem.recindex 1247 recoffset = recindex * btrecord_size 1248 record = kern.GetValueFromAddress(btrecords + recoffset, 'btlog_record_t *') 1249 out_str = ('-' * 8) 1250 if record.operation == 1: 1251 out_str += "OP: ALLOC. " 1252 else: 1253 out_str += "OP: FREE. " 1254 out_str += "Stack Index {0: <d} {1: <s}\n".format(recindex, ('-' * 8)) 1255 print out_str 1256 print GetBtlogBacktrace(depth, record) 1257 print " \n" 1258 if int(record.operation) == prev_op: 1259 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8)) 1260 return 1261 prev_op = int(record.operation) 1262 scan_items = 0 1263 hashelem = cast(hashelem.element_hash_link.tqe_next, 'btlog_element_t *') 1264 scan_items += 1 1265 if scan_items % 100 == 0: 1266 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items) 1267 1268# EndMacro: zstack_findelem 1269 1270@lldb_command('zstack_findtop', 'N:') 1271def ShowZstackTop(cmd_args=None, cmd_options={}): 1272 """ Zone leak debugging: search the log and print the stacks with the most active references 1273 in the stack trace. 1274 1275 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr> 1276 """ 1277 1278 if not cmd_args: 1279 raise ArgumentError('Missing required btlog address argument') 1280 1281 n = 5 1282 if '-N' in cmd_options: 1283 n = int(cmd_options['-N']) 1284 1285 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *') 1286 btrecord_size = unsigned(btlog_ptr.btrecord_size) 1287 btrecords = unsigned(btlog_ptr.btrecords) 1288 1289 cpcs_index = unsigned(btlog_ptr.head) 1290 depth = unsigned(btlog_ptr.btrecord_btdepth) 1291 1292 records = [] 1293 while cpcs_index != 0xffffff: 1294 cpcs_record_offset = cpcs_index * btrecord_size 1295 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *') 1296 cpcs_record.index = cpcs_index 1297 records.append(cpcs_record) 1298 cpcs_index = cpcs_record.next 1299 1300 recs = sorted(records, key=lambda x: x.ref_count, reverse=True) 1301 1302 for rec in recs[:n]: 1303 ShowZStackRecord(rec, rec.index, depth, unsigned(btlog_ptr.active_element_count)) 1304 1305# EndMacro: zstack_findtop 1306 1307# Macro: btlog_find 1308 1309@lldb_command('btlog_find', "AS") 1310def BtlogFind(cmd_args=None, cmd_options={}): 1311 """ 1312 """ 1313 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead." 1314 return 1315 1316#EndMacro: btlog_find 1317 1318#Macro: showzalloc 1319 1320@lldb_command('showzalloc') 1321def ShowZalloc(cmd_args=None): 1322 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace. 1323 Usage: showzalloc <index> 1324 """ 1325 if not cmd_args: 1326 print ShowZalloc.__doc__ 1327 return 1328 if unsigned(kern.globals.zallocations) == 0: 1329 print "zallocations array not initialized!" 1330 return 1331 zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])] 1332 print zallocation 1333 ShowZTrace([str(int(zallocation.za_trace_index))]) 1334 1335#EndMacro: showzalloc 1336 1337#Macro: showztrace 1338 1339@lldb_command('showztrace') 1340def ShowZTrace(cmd_args=None): 1341 """ Prints the backtrace from the ztraces array at index 1342 Usage: showztrace <trace index> 1343 """ 1344 if not cmd_args: 1345 print ShowZTrace.__doc__ 1346 return 1347 if unsigned(kern.globals.ztraces) == 0: 1348 print "ztraces array not initialized!" 1349 return 1350 ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])] 1351 print ztrace_addr 1352 ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth) 1353 1354#EndMacro: showztrace 1355 1356#Macro: showztraceaddr 1357 1358@lldb_command('showztraceaddr') 1359def ShowZTraceAddr(cmd_args=None): 1360 """ Prints the struct ztrace passed in. 1361 Usage: showztraceaddr <trace address> 1362 """ 1363 if not cmd_args: 1364 print ShowZTraceAddr.__doc__ 1365 return 1366 ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *') 1367 print dereference(ztrace_ptr) 1368 ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth) 1369 1370#EndMacro: showztraceaddr 1371 1372#Macro: showzstacktrace 1373 1374@lldb_command('showzstacktrace') 1375def ShowZstackTrace(cmd_args=None): 1376 """ Routine to print a stacktrace stored by OSBacktrace. 1377 Usage: showzstacktrace <saved stacktrace> [size] 1378 1379 size is optional, defaults to 15. 1380 """ 1381 if not cmd_args: 1382 print ShowZstackTrace.__doc__ 1383 return 1384 void_ptr_type = gettype('void *') 1385 void_double_ptr_type = void_ptr_type.GetPointerType() 1386 trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type) 1387 trace_size = 15 1388 if len(cmd_args) >= 2: 1389 trace_size = ArgumentStringToInt(cmd_args[1]) 1390 ShowZstackTraceHelper(trace, trace_size) 1391 1392#EndMacro: showzstacktrace 1393 1394def ShowZstackTraceHelper(stack, depth): 1395 """ Helper routine for printing a zstack. 1396 params: 1397 stack: void *[] - An array of pointers representing the Zstack 1398 depth: int - The depth of the ztrace stack 1399 returns: 1400 None 1401 """ 1402 trace_current = 0 1403 while trace_current < depth: 1404 trace_addr = stack[trace_current] 1405 symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr)) 1406 if symbol_arr: 1407 symbol_str = str(symbol_arr[0].addr) 1408 else: 1409 symbol_str = '' 1410 print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str) 1411 trace_current += 1 1412 1413#Macro: showtopztrace 1414 1415@lldb_command('showtopztrace') 1416def ShowTopZtrace(cmd_args=None): 1417 """ Shows the ztrace with the biggest size. 1418 (According to top_ztrace, not by iterating through the hash table) 1419 """ 1420 top_trace = kern.globals.top_ztrace 1421 print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace')) 1422 print dereference(top_trace) 1423 ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth) 1424 1425#EndMacro: showtopztrace 1426 1427#Macro: showzallocs 1428 1429@lldb_command('showzallocs') 1430def ShowZallocs(cmd_args=None): 1431 """ Prints all allocations in the zallocations table 1432 """ 1433 if unsigned(kern.globals.zallocations) == 0: 1434 print "zallocations array not initialized!" 1435 return 1436 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE') 1437 current_index = 0 1438 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets) 1439 allocation_count = 0 1440 while current_index < max_zallocation: 1441 current_zalloc = kern.globals.zallocations[current_index] 1442 if int(current_zalloc.za_element) != 0: 1443 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)) 1444 allocation_count += 1 1445 current_index += 1 1446 print 'Total Allocations: {0: <d}'.format(allocation_count) 1447 1448#EndMacro: showzallocs 1449 1450#Macro: showzallocsfortrace 1451 1452@lldb_command('showzallocsfortrace') 1453def ShowZallocsForTrace(cmd_args=None): 1454 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table 1455 Usage: showzallocsfortrace <trace index> 1456 """ 1457 if not cmd_args: 1458 print ShowZallocsForTrace.__doc__ 1459 return 1460 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE') 1461 target_index = ArgumentStringToInt(cmd_args[0]) 1462 current_index = 0 1463 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets) 1464 allocation_count = 0 1465 while current_index < max_zallocation: 1466 current_zalloc = kern.globals.zallocations[current_index] 1467 if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)): 1468 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size) 1469 allocation_count += 1 1470 current_index += 1 1471 print 'Total Allocations: {0: <d}'.format(allocation_count) 1472 1473#EndMacro: showzallocsfortrace 1474 1475#Macro: showztraces 1476 1477@lldb_command('showztraces') 1478def ShowZTraces(cmd_args=None): 1479 """ Prints all traces with size > 0 1480 """ 1481 ShowZTracesAbove([0]) 1482 1483#EndMacro: showztraces 1484 1485#Macro: showztracesabove 1486 1487@lldb_command('showztracesabove') 1488def ShowZTracesAbove(cmd_args=None): 1489 """ Prints all traces with size greater than X 1490 Usage: showztracesabove <size> 1491 """ 1492 if not cmd_args: 1493 print ShowZTracesAbove.__doc__ 1494 return 1495 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE') 1496 current_index = 0 1497 ztrace_count = 0 1498 max_ztrace = unsigned(kern.globals.zleak_trace_buckets) 1499 while current_index < max_ztrace: 1500 ztrace_current = kern.globals.ztraces[current_index] 1501 if ztrace_current.zt_size > unsigned(cmd_args[0]): 1502 print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size)) 1503 ztrace_count += 1 1504 current_index += 1 1505 print 'Total traces: {0: <d}'.format(ztrace_count) 1506 1507#EndMacro: showztracesabove 1508 1509#Macro: showztracehistogram 1510 1511@lldb_command('showztracehistogram') 1512def ShowZtraceHistogram(cmd_args=None): 1513 """ Prints the histogram of the ztrace table 1514 """ 1515 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS') 1516 current_index = 0 1517 ztrace_count = 0 1518 max_ztrace = unsigned(kern.globals.zleak_trace_buckets) 1519 while current_index < max_ztrace: 1520 ztrace_current = kern.globals.ztraces[current_index] 1521 if ztrace_current.zt_hit_count != 0: 1522 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions) 1523 ztrace_count += 1 1524 current_index += 1 1525 print 'Total traces: {0: <d}'.format(ztrace_count) 1526 1527#EndMacro: showztracehistogram 1528 1529#Macro: showzallochistogram 1530 1531@lldb_command('showzallochistogram') 1532def ShowZallocHistogram(cmd_args=None): 1533 """ Prints the histogram for the zalloc table 1534 """ 1535 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT') 1536 current_index = 0 1537 zallocation_count = 0 1538 max_ztrace = unsigned(kern.globals.zleak_alloc_buckets) 1539 while current_index < max_ztrace: 1540 zallocation_current = kern.globals.zallocations[current_index] 1541 if zallocation_current.za_hit_count != 0: 1542 print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count) 1543 zallocation_count += 1 1544 current_index += 1 1545 print 'Total Allocations: {0: <d}'.format(zallocation_count) 1546 1547#EndMacro: showzallochistogram 1548 1549#Macro: showzstats 1550 1551@lldb_command('showzstats') 1552def ShowZstats(cmd_args=None): 1553 """ Prints the zone leak detection stats 1554 """ 1555 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions)) 1556 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites)) 1557 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded)) 1558 1559#EndMacro: showzstats 1560 1561#Macro: showpcpu 1562 1563@lldb_command('showpcpu', "N:V", fancy=True) 1564def ShowPCPU(cmd_args=None, cmd_options={}, O=None): 1565 """ Show per-cpu variables 1566 usage: showpcpu [-N <cpu>] [-V] <variable name> 1567 1568 Use -N <cpu> to only dump the value for a given CPU number 1569 Use -V to dump the values of the variables after their addresses 1570 """ 1571 1572 if not cmd_args: 1573 raise ArgumentError("No arguments passed") 1574 1575 cpu = None 1576 ncpu = kern.globals.zpercpu_early_count 1577 pcpu_base = kern.globals.percpu_base 1578 1579 if "-N" in cmd_options: 1580 cpu = unsigned(int(cmd_options["-N"])) 1581 if cpu >= unsigned(ncpu): 1582 raise ArgumentError("Invalid cpu {d}".format(cpu)) 1583 1584 var = addressof(kern.GetGlobalVariable('percpu_slot_' + cmd_args[0])) 1585 ty = var.GetSBValue().GetTypeName() 1586 1587 r = range(0, ncpu) 1588 if cpu is not None: 1589 r = range(cpu, cpu + 1) 1590 1591 def PCPUSlot(pcpu_var, i): 1592 addr = unsigned(pcpu_var) + kern.PERCPU_BASE(i) 1593 return kern.GetValueFromAddress(addr, pcpu_var) 1594 1595 with O.table("{:<4s} {:<20s}".format("CPU", "address")): 1596 for i in r: 1597 print O.format("{:<4d} ({:s}){:#x}", i, ty, PCPUSlot(var, i)) 1598 1599 if not "-V" in cmd_options: 1600 return 1601 1602 for i in r: 1603 with O.table("CPU {:d}".format(i)): 1604 print dereference(PCPUSlot(var, i)) 1605 1606#EndMacro: showpcpu 1607 1608def GetBtlogBacktrace(depth, zstack_record): 1609 """ Helper routine for getting a BT Log record backtrace stack. 1610 params: 1611 depth:int - The depth of the zstack record 1612 zstack_record:btlog_record_t * - A BTLog record 1613 returns: 1614 str - string with backtrace in it. 1615 """ 1616 out_str = '' 1617 frame = 0 1618 if not zstack_record: 1619 return "Zstack record none!" 1620 1621 depth_val = unsigned(depth) 1622 while frame < depth_val: 1623 frame_pc = zstack_record.bt[frame] 1624 if not frame_pc or int(frame_pc) == 0: 1625 break 1626 symbol_arr = kern.SymbolicateFromAddress(frame_pc) 1627 if symbol_arr: 1628 symbol_str = str(symbol_arr[0].addr) 1629 else: 1630 symbol_str = '' 1631 out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str) 1632 frame += 1 1633 return out_str 1634 1635def ShowZStackRecord(zstack_record, zstack_index, btrecord_btdepth, elements_count): 1636 """ Helper routine for printing a single zstack record 1637 params: 1638 zstack_record:btlog_record_t * - A BTLog record 1639 zstack_index:int - Index for the record in the BTLog table 1640 returns: 1641 None 1642 """ 1643 out_str = ('-' * 8) 1644 if zstack_record.operation == 1: 1645 out_str += "ALLOC. " 1646 else: 1647 out_str += "FREE. " 1648 out_str += "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index, zstack_record.ref_count, elements_count, ('-' * 8)) 1649 print out_str 1650 print GetBtlogBacktrace(btrecord_btdepth, zstack_record) 1651 print " \n" 1652 1653# Macro: showioalloc 1654 1655@lldb_command('showioalloc') 1656def ShowIOAllocations(cmd_args=None): 1657 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details. 1658 Routine to display a summary of memory accounting allocated by IOKit allocators. 1659 """ 1660 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024)) 1661 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024)) 1662 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024)) 1663 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024)) 1664 1665 1666# EndMacro: showioalloc 1667 1668 1669# Macro: showselectmem 1670@lldb_command('showselectmem', "S:") 1671def ShowSelectMem(cmd_args=None, cmd_options={}): 1672 """ Show memory cached by threads on calls to select. 1673 1674 usage: showselectmem [-v] 1675 -v : print each thread's memory 1676 (one line per thread with non-zero select memory) 1677 -S {addr} : Find the thread whose thread-local select set 1678 matches the given address 1679 """ 1680 verbose = False 1681 opt_wqs = 0 1682 if config['verbosity'] > vHUMAN: 1683 verbose = True 1684 if "-S" in cmd_options: 1685 opt_wqs = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *')) 1686 if opt_wqs == 0: 1687 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options["-S"])) 1688 selmem = 0 1689 if verbose: 1690 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)') 1691 for t in kern.tasks: 1692 for th in IterateQueue(t.threads, 'thread *', 'task_threads'): 1693 uth = GetBSDThread(th) 1694 wqs = 0 1695 if hasattr(uth, 'uu_allocsize'): # old style 1696 thmem = uth.uu_allocsize 1697 wqs = uth.uu_wqset 1698 elif hasattr(uth, 'uu_wqstate_sz'): # new style 1699 thmem = uth.uu_wqstate_sz 1700 wqs = uth.uu_wqset 1701 else: 1702 print "What kind of uthread is this?!" 1703 return 1704 if opt_wqs and opt_wqs == unsigned(wqs): 1705 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs, unsigned(th), unsigned(th.thread_id)) 1706 if verbose and thmem > 0: 1707 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t), unsigned(th.thread_id), thmem) 1708 selmem += thmem 1709 print '-'*40 1710 print "Total: {:d} bytes ({:d} kbytes)".format(selmem, selmem/1024) 1711# Endmacro: showselectmem 1712 1713 1714# Macro: showtaskvme 1715@lldb_command('showtaskvme', "PS") 1716def ShowTaskVmeHelper(cmd_args=None, cmd_options={}): 1717 """ Display a summary list of the specified vm_map's entries 1718 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 ) 1719 Use -S flag to show VM object shadow chains 1720 Use -P flag to show pager info (mapped file, compressed pages, ...) 1721 """ 1722 show_pager_info = False 1723 show_all_shadows = False 1724 if "-P" in cmd_options: 1725 show_pager_info = True 1726 if "-S" in cmd_options: 1727 show_all_shadows = True 1728 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 1729 ShowTaskVMEntries(task, show_pager_info, show_all_shadows) 1730 1731@lldb_command('showallvme', "PS") 1732def ShowAllVME(cmd_args=None, cmd_options={}): 1733 """ Routine to print a summary listing of all the vm map entries 1734 Go Through each task in system and show the vm memory regions 1735 Use -S flag to show VM object shadow chains 1736 Use -P flag to show pager info (mapped file, compressed pages, ...) 1737 """ 1738 show_pager_info = False 1739 show_all_shadows = False 1740 if "-P" in cmd_options: 1741 show_pager_info = True 1742 if "-S" in cmd_options: 1743 show_all_shadows = True 1744 for task in kern.tasks: 1745 ShowTaskVMEntries(task, show_pager_info, show_all_shadows) 1746 1747@lldb_command('showallvm') 1748def ShowAllVM(cmd_args=None): 1749 """ Routine to print a summary listing of all the vm maps 1750 """ 1751 for task in kern.tasks: 1752 print GetTaskSummary.header + ' ' + GetProcSummary.header 1753 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *')) 1754 print GetVMMapSummary.header 1755 print GetVMMapSummary(task.map) 1756 1757@lldb_command("showtaskvm") 1758def ShowTaskVM(cmd_args=None): 1759 """ Display info about the specified task's vm_map 1760 syntax: (lldb) showtaskvm <task_ptr> 1761 """ 1762 if not cmd_args: 1763 print ShowTaskVM.__doc__ 1764 return False 1765 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 1766 if not task: 1767 print "Unknown arguments." 1768 return False 1769 print GetTaskSummary.header + ' ' + GetProcSummary.header 1770 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *')) 1771 print GetVMMapSummary.header 1772 print GetVMMapSummary(task.map) 1773 return True 1774 1775def GetLedgerEntryBalance(template, ledger, idx): 1776 entry = GetLedgerEntryWithTemplate(template, ledger, idx) 1777 return entry['balance'] 1778 1779@lldb_command('showallvmstats') 1780def ShowAllVMStats(cmd_args=None): 1781 """ Print a summary of vm statistics in a table format 1782 """ 1783 page_size = kern.globals.page_size 1784 vmstats = lambda:None 1785 vmstats.wired_count = 0 1786 vmstats.resident_count = 0 1787 vmstats.resident_max = 0 1788 vmstats.internal = 0 1789 vmstats.external = 0 1790 vmstats.reusable = 0 1791 vmstats.compressed = 0 1792 vmstats.compressed_peak = 0 1793 vmstats.compressed_lifetime = 0 1794 vmstats.error = '' 1795 1796 hdr_format = "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}" 1797 print hdr_format.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '') 1798 print hdr_format.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '') 1799 entry_format = "{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} {1: >10d} {0: <32s} {s.error}" 1800 1801 ledger_template = kern.globals.task_ledger_template 1802 entry_indices = {} 1803 entry_indices['wired_mem'] = GetLedgerEntryIndex(ledger_template, 'wired_mem') 1804 assert(entry_indices['wired_mem'] != -1) 1805 entry_indices['phys_mem'] = GetLedgerEntryIndex(ledger_template, 'phys_mem') 1806 assert(entry_indices['phys_mem'] != -1) 1807 entry_indices['internal'] = GetLedgerEntryIndex(ledger_template, 'internal') 1808 assert(entry_indices['internal'] != -1) 1809 entry_indices['external'] = GetLedgerEntryIndex(ledger_template, 'external') 1810 assert(entry_indices['external'] != -1) 1811 entry_indices['reusable'] = GetLedgerEntryIndex(ledger_template, 'reusable') 1812 assert(entry_indices['reusable'] != -1) 1813 entry_indices['internal_compressed'] = GetLedgerEntryIndex(ledger_template, 'internal_compressed') 1814 assert(entry_indices['internal_compressed'] != -1) 1815 1816 for task in kern.tasks: 1817 proc = Cast(task.bsd_info, 'proc *') 1818 vmmap = Cast(task.map, '_vm_map *') 1819 page_size = 1 << int(vmmap.hdr.page_shift) 1820 task_ledgerp = task.ledger 1821 vmstats.error = '' 1822 def GetLedgerEntryBalancePages(template, ledger, index): 1823 return GetLedgerEntryBalance(template, ledger, index) / page_size 1824 vmstats.wired_count = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['wired_mem']) 1825 vmstats.resident_count = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['phys_mem']) 1826 vmstats.resident_max = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['phys_mem'])['lifetime_max'] / page_size 1827 vmstats.internal = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['internal']) 1828 vmstats.external = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['external']) 1829 vmstats.reusable = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['reusable']) 1830 vmstats.compressed = GetLedgerEntryBalancePages(ledger_template, task_ledgerp, entry_indices['internal_compressed']) 1831 vmstats.compressed_peak = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['internal_compressed'])['lifetime_max'] / page_size 1832 vmstats.compressed_lifetime = GetLedgerEntryWithTemplate(ledger_template, task_ledgerp, entry_indices['internal_compressed'])['credit'] / page_size 1833 vmstats.new_resident_count = vmstats.internal + vmstats.external 1834 1835 if vmstats.internal < 0: 1836 vmstats.error += '*' 1837 if vmstats.external < 0: 1838 vmstats.error += '*' 1839 if vmstats.reusable < 0: 1840 vmstats.error += '*' 1841 if vmstats.compressed < 0: 1842 vmstats.error += '*' 1843 if vmstats.compressed_peak < 0: 1844 vmstats.error += '*' 1845 if vmstats.compressed_lifetime < 0: 1846 vmstats.error += '*' 1847 if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count: 1848 vmstats.error += '*' 1849 1850 print entry_format.format(GetProcName(proc), GetProcPID(proc), p=proc, m=vmmap, vsize=(unsigned(vmmap.size) / page_size), t=task, s=vmstats) 1851 1852 1853def ShowTaskVMEntries(task, show_pager_info, show_all_shadows): 1854 """ Routine to print out a summary listing of all the entries in a vm_map 1855 params: 1856 task - core.value : a object of type 'task *' 1857 returns: 1858 None 1859 """ 1860 print "vm_map entries for task " + hex(task) 1861 print GetTaskSummary.header 1862 print GetTaskSummary(task) 1863 if not task.map: 1864 print "Task {0: <#020x} has map = 0x0" 1865 return None 1866 print GetVMMapSummary.header 1867 print GetVMMapSummary(task.map) 1868 vme_list_head = task.map.hdr.links 1869 vme_ptr_type = GetType('vm_map_entry *') 1870 print GetVMEntrySummary.header 1871 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 1872 print GetVMEntrySummary(vme, show_pager_info, show_all_shadows) 1873 return None 1874 1875@lldb_command("showmap") 1876def ShowMap(cmd_args=None): 1877 """ Routine to print out info about the specified vm_map 1878 usage: showmap <vm_map> 1879 """ 1880 if cmd_args == None or len(cmd_args) < 1: 1881 print "Invalid argument.", ShowMap.__doc__ 1882 return 1883 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 1884 print GetVMMapSummary.header 1885 print GetVMMapSummary(map_val) 1886 1887@lldb_command("showmapvme") 1888def ShowMapVME(cmd_args=None): 1889 """Routine to print out info about the specified vm_map and its vm entries 1890 usage: showmapvme <vm_map> 1891 """ 1892 if cmd_args == None or len(cmd_args) < 1: 1893 print "Invalid argument.", ShowMapVME.__doc__ 1894 return 1895 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 1896 print GetVMMapSummary.header 1897 print GetVMMapSummary(map_val) 1898 vme_list_head = map_val.hdr.links 1899 vme_ptr_type = GetType('vm_map_entry *') 1900 print GetVMEntrySummary.header 1901 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 1902 print GetVMEntrySummary(vme) 1903 return None 1904 1905def GetResidentPageCount(vmmap): 1906 resident_pages = 0 1907 ledger_template = kern.globals.task_ledger_template 1908 if vmmap.pmap != 0 and vmmap.pmap != kern.globals.kernel_pmap: 1909 idx = GetLedgerEntryIndex(ledger_template, "phys_mem") 1910 phys_mem = GetLedgerEntryBalance(ledger_template, vmmap.pmap.ledger, idx) 1911 resident_pages = phys_mem / kern.globals.page_size 1912 return resident_pages 1913 1914@lldb_type_summary(['_vm_map *', 'vm_map_t']) 1915@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s} {7: <7s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free", "pgshift")) 1916def GetVMMapSummary(vmmap): 1917 """ Display interesting bits from vm_map struct """ 1918 out_string = "" 1919 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x} {7: >7d}" 1920 vm_size = uint64_t(vmmap.size).value 1921 resident_pages = GetResidentPageCount(vmmap) 1922 first_free = 0 1923 if int(vmmap.holelistenabled) == 0: first_free = vmmap.f_s._first_free 1924 out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, first_free, vmmap.hdr.page_shift) 1925 return out_string 1926 1927@lldb_type_summary(['vm_map_entry']) 1928@header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset")) 1929def GetVMEntrySummary(vme): 1930 """ Display vm entry specific information. """ 1931 page_size = kern.globals.page_size 1932 out_string = "" 1933 format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}" 1934 vme_protection = int(vme.protection) 1935 vme_max_protection = int(vme.max_protection) 1936 vme_extra_info_str ="SC-Ds"[int(vme.inheritance)] 1937 if int(vme.is_sub_map) != 0 : 1938 vme_extra_info_str +="s" 1939 elif int(vme.needs_copy) != 0 : 1940 vme_extra_info_str +="n" 1941 num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) / page_size 1942 out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.vme_object.vmo_object, vme.vme_offset) 1943 return out_string 1944 1945# EndMacro: showtaskvme 1946@lldb_command('showmapwired') 1947def ShowMapWired(cmd_args=None): 1948 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map 1949 """ 1950 if cmd_args == None or len(cmd_args) < 1: 1951 print "Invalid argument", ShowMapWired.__doc__ 1952 return 1953 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 1954 1955 1956@lldb_type_summary(['kmod_info_t *']) 1957@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name')) 1958def GetKextSummary(kmod): 1959 """ returns a string representation of kext information 1960 """ 1961 out_string = "" 1962 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}" 1963 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod.address), unsigned(kmod.size)) 1964 text_segment = macho.get_text_segment(segments) 1965 if not text_segment: 1966 text_segment = segments[0] 1967 out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, text_segment.vmaddr, text_segment.vmsize, kmod.version, kmod.name) 1968 return out_string 1969 1970@lldb_type_summary(['uuid_t']) 1971@header("") 1972def GetUUIDSummary(uuid): 1973 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26 1974 """ 1975 arr = Cast(addressof(uuid), 'uint8_t *') 1976 data = [] 1977 for i in range(16): 1978 data.append(int(arr[i])) 1979 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) 1980 1981@lldb_command('showallkmods') 1982def ShowAllKexts(cmd_args=None): 1983 """Display a summary listing of all loaded kexts (alias: showallkmods) 1984 """ 1985 kmod_val = kern.globals.kmod 1986 kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN)) 1987 print "{: <36s} ".format("UUID") + GetKextSummary.header 1988 for kval in IterateLinkedList(kmod_val, 'next'): 1989 uuid = "........-....-....-....-............" 1990 kaddr = unsigned(kval.address) 1991 found_kext_summary = None 1992 for l in kextuuidinfo : 1993 if kaddr == int(l[3],16): 1994 uuid = l[0] 1995 found_kext_summary = l 1996 break 1997 if found_kext_summary: 1998 _ksummary = GetKextSummary(found_kext_summary[7]) 1999 else: 2000 _ksummary = GetKextSummary(kval) 2001 print uuid + " " + _ksummary 2002 2003def GetKmodWithAddr(addr): 2004 """ Go through kmod list and find one with begin_addr as addr 2005 returns: None if not found. else a cvalue of type kmod 2006 """ 2007 kmod_val = kern.globals.kmod 2008 for kval in IterateLinkedList(kmod_val, 'next'): 2009 if addr == unsigned(kval.address): 2010 return kval 2011 return None 2012 2013def GetAllSegmentsAndSectionsFromDataInMemory(address, size): 2014 """ reads memory at address and parses mach_header to get segment and section information 2015 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...]) 2016 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize' 2017 if TEXT segment is not found a dummy segment & section with address, size is returned. 2018 """ 2019 cache_hash = "kern.kexts.segments.{}.{}".format(address, size) 2020 cached_result = caching.GetDynamicCacheData(cache_hash,()) 2021 if cached_result: 2022 return cached_result 2023 2024 defval = macho.MachOSegment('__TEXT', address, size, 0, size) 2025 if address == 0 or size == 0: 2026 return ([defval], [defval]) 2027 2028 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2: 2029 # until we have separate version. we will pay penalty only on arm64 devices 2030 if not kern.arch.startswith('arm64'): 2031 return ([defval], [defval]) 2032 2033 restrict_size_to_read = 1536 2034 machoObject = None 2035 while machoObject is None: 2036 err = lldb.SBError() 2037 size_to_read = min(size, restrict_size_to_read) 2038 data = LazyTarget.GetProcess().ReadMemory(address, size_to_read, err) 2039 if not err.Success(): 2040 print "Failed to read memory at {} and size {}".format(address, size_to_read) 2041 return ([defval], [defval]) 2042 try: 2043 m = macho.MemMacho(data, len(data)) 2044 machoObject = m 2045 except Exception as e: 2046 if str(e.message).find('unpack requires a string argument') >= 0: 2047 # this may be due to short read of memory. Lets do double read size. 2048 restrict_size_to_read *= 2 2049 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read)) 2050 continue 2051 else: 2052 print "Failed to read MachO for address {} errormessage: {}".format(address, e.message) 2053 return ([defval], [defval]) 2054 # end of while loop. We have machoObject defined 2055 segments = machoObject.get_segments_with_name('') 2056 sections = machoObject.get_sections_with_name('') 2057 rval = (segments, sections) 2058 caching.SaveDynamicCacheData(cache_hash, rval) 2059 return rval 2060 2061def GetKextLoadInformation(addr=0, show_progress=False): 2062 """ Extract the kext uuid and load address information from the kernel data structure. 2063 params: 2064 addr - int - optional integer that is the address to search for. 2065 returns: 2066 [] - array with each entry of format 2067 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name', 2068 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj) 2069 """ 2070 cached_result = caching.GetDynamicCacheData("kern.kexts.loadinformation", []) 2071 ## if specific addr is provided then ignore caching 2072 if cached_result and not addr: 2073 return cached_result 2074 2075 # because of <rdar://problem/12683084>, we can't find summaries directly 2076 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries)) 2077 baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10 2078 summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *') 2079 total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries) 2080 kext_version = int(kern.globals.gLoadedKextSummaries.version) 2081 entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4 2082 if kext_version >= 2 : 2083 entry_size = int(kern.globals.gLoadedKextSummaries.entry_size) 2084 retval = [] 2085 for i in range(total_summaries): 2086 if show_progress: 2087 print "progress: {}/{}".format(i, total_summaries) 2088 tmpaddress = unsigned(summaries_begin) + (i * entry_size) 2089 current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *') 2090 # code to extract macho information 2091 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext.address), unsigned(current_kext.size)) 2092 seginfo = macho.get_text_segment(segments) 2093 if not seginfo: 2094 seginfo = segments[0] 2095 kmod_obj = GetKmodWithAddr(unsigned(current_kext.address)) 2096 if addr != 0 : 2097 if addr == unsigned(current_kext.address) or addr == seginfo.vmaddr: 2098 return [(GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)] 2099 retval.append((GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)) 2100 2101 if not addr: 2102 caching.SaveDynamicCacheData("kern.kexts.loadinformation", retval) 2103 return retval 2104 2105lldb_alias('showallkexts', 'showallkmods') 2106 2107def GetOSKextVersion(version_num): 2108 """ returns a string of format 1.2.3x from the version_num 2109 params: version_num - int 2110 return: str 2111 """ 2112 if version_num == -1 : 2113 return "invalid" 2114 (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000) 2115 version = version_num 2116 2117 vers_major = version / MAJ_MULT 2118 version = version - (vers_major * MAJ_MULT) 2119 2120 vers_minor = version / MIN_MULT 2121 version = version - (vers_minor * MIN_MULT) 2122 2123 vers_revision = version / REV_MULT 2124 version = version - (vers_revision * REV_MULT) 2125 2126 vers_stage = version / STAGE_MULT 2127 version = version - (vers_stage * STAGE_MULT) 2128 2129 vers_stage_level = version 2130 2131 out_str = "%d.%d" % (vers_major, vers_minor) 2132 if vers_revision > 0: out_str += ".%d" % vers_revision 2133 if vers_stage == 1 : out_str += "d%d" % vers_stage_level 2134 if vers_stage == 3 : out_str += "a%d" % vers_stage_level 2135 if vers_stage == 5 : out_str += "b%d" % vers_stage_level 2136 if vers_stage == 6 : out_str += "fc%d" % vers_stage_level 2137 2138 return out_str 2139 2140@lldb_command('showallknownkmods') 2141def ShowAllKnownKexts(cmd_args=None): 2142 """ Display a summary listing of all kexts known in the system. 2143 This is particularly useful to find if some kext was unloaded before this crash'ed state. 2144 """ 2145 kext_count = int(kern.globals.sKextsByID.count) 2146 index = 0 2147 kext_dictionary = kern.globals.sKextsByID.dictionary 2148 print "%d kexts in sKextsByID:" % kext_count 2149 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name') 2150 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}" 2151 2152 while index < kext_count: 2153 kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index) 2154 kext_name = str(kext_dict.key.string) 2155 osk = Cast(kext_dict.value, 'OSKext *') 2156 if int(osk.flags.loaded) : 2157 load_addr = "{0: <#020x}".format(osk.kmod_info) 2158 id = "{0: >5d}".format(osk.loadTag) 2159 else: 2160 load_addr = "------" 2161 id = "--" 2162 version_num = unsigned(osk.version) 2163 version = GetOSKextVersion(version_num) 2164 print format_string.format(osk, load_addr, id, version, kext_name) 2165 index += 1 2166 2167 return 2168 2169def FindKmodNameForAddr(addr): 2170 """ Given an address, return the name of the kext containing that address 2171 """ 2172 addr = unsigned(addr) 2173 all_kexts_info = GetKextLoadInformation() 2174 for kinfo in all_kexts_info: 2175 segment = macho.get_segment_with_addr(kinfo[4], addr) 2176 if segment: 2177 return kinfo[7].name 2178 return None 2179 2180 2181@lldb_command('addkextaddr') 2182def AddKextAddr(cmd_args=[]): 2183 """ Given an address, load the kext which contains that address 2184 Syntax: (lldb) addkextaddr <addr> 2185 """ 2186 if len(cmd_args) < 1: 2187 raise ArgumentError("Insufficient arguments") 2188 2189 addr = ArgumentStringToInt(cmd_args[0]) 2190 all_kexts_info = GetKextLoadInformation() 2191 kernel_uuid = str(kern.globals.kernel_uuid_string).lower() 2192 found_kinfo = None 2193 found_segment = None 2194 for kinfo in all_kexts_info: 2195 segment = macho.get_segment_with_addr(kinfo[4], addr) 2196 if segment: 2197 print GetKextSummary.header 2198 print GetKextSummary(kinfo[7]) + " segment: {} offset = {:#0x}".format(segment.name, (addr - segment.vmaddr)) 2199 cur_uuid = kinfo[0].lower() 2200 if (kernel_uuid == cur_uuid): 2201 print "(builtin)" 2202 else: 2203 print "Fetching dSYM for %s" % cur_uuid 2204 info = dsymForUUID(cur_uuid) 2205 if info and 'DBGSymbolRichExecutable' in info: 2206 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable']) 2207 addDSYM(cur_uuid, info) 2208 loadDSYM(cur_uuid, int(kinfo[1],16), kinfo[4]) 2209 else: 2210 print "Failed to get symbol info for %s" % cur_uuid 2211 return 2212 2213 2214@lldb_command('showkmodaddr') 2215def ShowKmodAddr(cmd_args=[]): 2216 """ Given an address, print the offset and name for the kmod containing it 2217 Syntax: (lldb) showkmodaddr <addr> 2218 """ 2219 if len(cmd_args) < 1: 2220 raise ArgumentError("Insufficient arguments") 2221 2222 addr = ArgumentStringToInt(cmd_args[0]) 2223 all_kexts_info = GetKextLoadInformation() 2224 found_kinfo = None 2225 found_segment = None 2226 for kinfo in all_kexts_info: 2227 s = macho.get_segment_with_addr(kinfo[4], addr) 2228 if s: 2229 found_segment = s 2230 found_kinfo = kinfo 2231 break 2232 if found_kinfo: 2233 print GetKextSummary.header 2234 print GetKextSummary(found_kinfo[7]) + " segment: {} offset = {:#0x}".format(found_segment.name, (addr - found_segment.vmaddr)) 2235 return True 2236 return False 2237 2238 2239@lldb_command('addkext','AF:N:') 2240def AddKextSyms(cmd_args=[], cmd_options={}): 2241 """ Add kext symbols into lldb. 2242 This command finds symbols for a uuid and load the required executable 2243 Usage: 2244 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E 2245 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address 2246 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto 2247 addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A 2248 addkext all : Will load all the kext symbols - SLOW 2249 """ 2250 2251 2252 if "-F" in cmd_options: 2253 exec_path = cmd_options["-F"] 2254 exec_full_path = ResolveFSPath(exec_path) 2255 if not os.path.exists(exec_full_path): 2256 raise ArgumentError("Unable to resolve {:s}".format(exec_path)) 2257 2258 if not os.path.isfile(exec_full_path): 2259 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\ 2260\nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\ 2261\nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path)) 2262 2263 slide_value = None 2264 sections = None 2265 if cmd_args: 2266 slide_value = cmd_args[0] 2267 debuglog("loading slide value from user input {:s}".format(cmd_args[0])) 2268 2269 filespec = lldb.SBFileSpec(exec_full_path, False) 2270 print "target modules add \"{:s}\"".format(exec_full_path) 2271 print lldb_run_command("target modules add \"{:s}\"".format(exec_full_path)) 2272 loaded_module = LazyTarget.GetTarget().FindModule(filespec) 2273 if loaded_module.IsValid(): 2274 uuid_str = loaded_module.GetUUIDString() 2275 debuglog("added module {:s} with uuid {:s}".format(exec_full_path, uuid_str)) 2276 if slide_value is None: 2277 all_kexts_info = GetKextLoadInformation() 2278 for k in all_kexts_info: 2279 debuglog(k[0]) 2280 if k[0].lower() == uuid_str.lower(): 2281 slide_value = k[1] 2282 sections = k[4] 2283 debuglog("found the slide {:s} for uuid {:s}".format(k[1], k[0])) 2284 if slide_value is None: 2285 raise ArgumentError("Unable to find load address for module described at {:s} ".format(exec_full_path)) 2286 2287 if not sections: 2288 cmd_str = "target modules load --file \"{:s}\" --slide {:s}".format(exec_full_path, str(slide_value)) 2289 debuglog(cmd_str) 2290 else: 2291 cmd_str = "target modules load --file \"{:s}\"".format(exec_full_path) 2292 sections_str = "" 2293 for s in sections: 2294 sections_str += " {:s} {:#0x} ".format(s.name, s.vmaddr) 2295 cmd_str += sections_str 2296 debuglog(cmd_str) 2297 2298 lldb.debugger.HandleCommand(cmd_str) 2299 2300 kern.symbolicator = None 2301 return True 2302 2303 all_kexts_info = GetKextLoadInformation() 2304 kernel_uuid = str(kern.globals.kernel_uuid_string).lower() 2305 2306 if "-N" in cmd_options: 2307 kext_name = cmd_options["-N"] 2308 kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True) 2309 if len(kext_name_matches) != 1 and "-A" not in cmd_options: 2310 print "Ambiguous match for name: {:s}".format(kext_name) 2311 if len(kext_name_matches) > 0: 2312 print "Options are:\n\t" + "\n\t".join(kext_name_matches) 2313 return 2314 debuglog("matched the kext to name {:s} and uuid {:s}".format(kext_name_matches[0], kext_name)) 2315 for cur_knm in kext_name_matches: 2316 for x in all_kexts_info: 2317 if cur_knm == x[2]: 2318 cur_uuid = x[0].lower() 2319 if (kernel_uuid == cur_uuid): 2320 print "(builtin)" 2321 else: 2322 print "Fetching dSYM for {:s}".format(cur_uuid) 2323 info = dsymForUUID(cur_uuid) 2324 if info and 'DBGSymbolRichExecutable' in info: 2325 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable']) 2326 addDSYM(cur_uuid, info) 2327 loadDSYM(cur_uuid, int(x[1],16), x[4]) 2328 else: 2329 print "Failed to get symbol info for {:s}".format(cur_uuid) 2330 break 2331 kern.symbolicator = None 2332 return 2333 2334 if len(cmd_args) < 1: 2335 raise ArgumentError("No arguments specified.") 2336 2337 uuid = cmd_args[0].lower() 2338 2339 load_all_kexts = False 2340 if uuid == "all": 2341 load_all_kexts = True 2342 2343 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0: 2344 raise ArgumentError("Unknown argument {:s}".format(uuid)) 2345 2346 for k_info in all_kexts_info: 2347 cur_uuid = k_info[0].lower() 2348 if load_all_kexts or (uuid == cur_uuid): 2349 if (kernel_uuid != cur_uuid): 2350 print "Fetching dSYM for {:s}".format(cur_uuid) 2351 info = dsymForUUID(cur_uuid) 2352 if info and 'DBGSymbolRichExecutable' in info: 2353 print "Adding dSYM ({:s}) for {:s}".format(cur_uuid, info['DBGSymbolRichExecutable']) 2354 addDSYM(cur_uuid, info) 2355 loadDSYM(cur_uuid, int(k_info[1],16), k_info[4]) 2356 else: 2357 print "Failed to get symbol info for {:s}".format(cur_uuid) 2358 #end of for loop 2359 kern.symbolicator = None 2360 return True 2361 2362 2363 2364lldb_alias('showkmod', 'showkmodaddr') 2365lldb_alias('showkext', 'showkmodaddr') 2366lldb_alias('showkextaddr', 'showkmodaddr') 2367 2368@lldb_type_summary(['mount *']) 2369@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s} {9: <30s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom', 'iosched supported')) 2370def GetMountSummary(mount): 2371 """ Display a summary of mount on the system 2372 """ 2373 out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " + 2374 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " + 2375 "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s} {iomode: <30s}").format(mnt=mount, vfs=mount.mnt_vfsstat, iomode=('Yes' if (mount.mnt_ioflags & 0x4) else 'No')) 2376 return out_string 2377 2378@lldb_command('showallmounts') 2379def ShowAllMounts(cmd_args=None): 2380 """ Print all mount points 2381 """ 2382 mntlist = kern.globals.mountlist 2383 print GetMountSummary.header 2384 for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'): 2385 print GetMountSummary(mnt) 2386 return 2387 2388lldb_alias('ShowAllVols', 'showallmounts') 2389 2390@lldb_command('systemlog') 2391def ShowSystemLog(cmd_args=None): 2392 """ Display the kernel's printf ring buffer """ 2393 msgbufp = kern.globals.msgbufp 2394 msg_size = int(msgbufp.msg_size) 2395 msg_bufx = int(msgbufp.msg_bufx) 2396 msg_bufr = int(msgbufp.msg_bufr) 2397 msg_bufc = msgbufp.msg_bufc 2398 msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size) 2399 2400 # the buffer is circular; start at the write pointer to end, 2401 # then from beginning to write pointer 2402 line = '' 2403 err = lldb.SBError() 2404 for i in range(msg_bufx, msg_size) + range(0, msg_bufx) : 2405 err.Clear() 2406 cbyte = msg_bufc_data.GetUnsignedInt8(err, i) 2407 if not err.Success() : 2408 raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString()) 2409 c = chr(cbyte) 2410 if c == '\0' : 2411 continue 2412 elif c == '\n' : 2413 print line 2414 line = '' 2415 else : 2416 line += c 2417 2418 if len(line) > 0 : 2419 print line 2420 2421 return 2422 2423@static_var('output','') 2424def _GetVnodePathName(vnode, vnodename): 2425 """ Internal function to get vnode path string from vnode structure. 2426 params: 2427 vnode - core.value 2428 vnodename - str 2429 returns Nothing. The output will be stored in the static variable. 2430 """ 2431 if not vnode: 2432 return 2433 if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0: 2434 if int(vnode.v_mount.mnt_vnodecovered): 2435 _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) ) 2436 else: 2437 _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name)) 2438 _GetVnodePathName.output += "/%s" % vnodename 2439 2440def GetVnodePath(vnode): 2441 """ Get string representation of the vnode 2442 params: vnodeval - value representing vnode * in the kernel 2443 return: str - of format /path/to/something 2444 """ 2445 out_str = '' 2446 if vnode: 2447 if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) : 2448 out_str += "/" 2449 else: 2450 _GetVnodePathName.output = '' 2451 if abs(vnode.v_name) != 0: 2452 _GetVnodePathName(vnode, str(vnode.v_name)) 2453 out_str += _GetVnodePathName.output 2454 else: 2455 out_str += 'v_name = NULL' 2456 _GetVnodePathName.output = '' 2457 return out_str 2458 2459 2460@lldb_command('showvnodepath') 2461def ShowVnodePath(cmd_args=None): 2462 """ Prints the path for a vnode 2463 usage: showvnodepath <vnode> 2464 """ 2465 if cmd_args != None and len(cmd_args) > 0 : 2466 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 2467 if vnode_val: 2468 print GetVnodePath(vnode_val) 2469 return 2470 2471# Macro: showvnodedev 2472def GetVnodeDevInfo(vnode): 2473 """ Internal function to get information from the device type vnodes 2474 params: vnode - value representing struct vnode * 2475 return: str - formatted output information for block and char vnode types passed as param 2476 """ 2477 vnodedev_output = "" 2478 vblk_type = GetEnumValue('vtype::VBLK') 2479 vchr_type = GetEnumValue('vtype::VCHR') 2480 if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type): 2481 devnode = Cast(vnode.v_data, 'devnode_t *') 2482 devnode_dev = devnode.dn_typeinfo.dev 2483 devnode_major = (devnode_dev >> 24) & 0xff 2484 devnode_minor = devnode_dev & 0x00ffffff 2485 2486 # boilerplate device information for a vnode 2487 vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode) 2488 vnodedev_output += "\n\t type:\t\t" 2489 if (vnode.v_type == vblk_type): 2490 vnodedev_output += "VBLK" 2491 if (vnode.v_type == vchr_type): 2492 vnodedev_output += "VCHR" 2493 vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name) 2494 vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor) 2495 vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode)) 2496 vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid) 2497 2498 # decode device specific data 2499 vnodedev_output += "\nDevice Specific Information:\t" 2500 if (vnode.v_type == vblk_type): 2501 vnodedev_output += "Sorry, I do not know how to decode block devices yet!" 2502 vnodedev_output += "\nMaybe you can write me!" 2503 2504 if (vnode.v_type == vchr_type): 2505 # Device information; this is scanty 2506 # range check 2507 if (devnode_major > 42) or (devnode_major < 0): 2508 vnodedev_output += "Invalid major #\n" 2509 # static assignments in conf 2510 elif (devnode_major == 0): 2511 vnodedev_output += "Console mux device\n" 2512 elif (devnode_major == 2): 2513 vnodedev_output += "Current tty alias\n" 2514 elif (devnode_major == 3): 2515 vnodedev_output += "NULL device\n" 2516 elif (devnode_major == 4): 2517 vnodedev_output += "Old pty slave\n" 2518 elif (devnode_major == 5): 2519 vnodedev_output += "Old pty master\n" 2520 elif (devnode_major == 6): 2521 vnodedev_output += "Kernel log\n" 2522 elif (devnode_major == 12): 2523 vnodedev_output += "Memory devices\n" 2524 # Statically linked dynamic assignments 2525 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')): 2526 vnodedev_output += "Cloning pty master not done\n" 2527 #GetVnodeDevCpty(devnode_major, devnode_minor) 2528 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')): 2529 vnodedev_output += "Cloning pty slave not done\n" 2530 #GetVnodeDevCpty(devnode_major, devnode_minor) 2531 else: 2532 vnodedev_output += "RESERVED SLOT\n" 2533 else: 2534 vnodedev_output += "{:#x} is not a device".format(vnode) 2535 return vnodedev_output 2536 2537@lldb_command('showvnodedev') 2538def ShowVnodeDev(cmd_args=None): 2539 """ Routine to display details of all vnodes of block and character device types 2540 Usage: showvnodedev <address of vnode> 2541 """ 2542 if not cmd_args: 2543 print "No arguments passed" 2544 print ShowVnodeDev.__doc__ 2545 return False 2546 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 2547 if not vnode_val: 2548 print "unknown arguments:", str(cmd_args) 2549 return False 2550 print GetVnodeDevInfo(vnode_val) 2551 2552# EndMacro: showvnodedev 2553 2554# Macro: showvnodelocks 2555def GetVnodeLock(lockf): 2556 """ Internal function to get information from the given advisory lock 2557 params: lockf - value representing v_lockf member in struct vnode * 2558 return: str - formatted output information for the advisory lock 2559 """ 2560 vnode_lock_output = '' 2561 lockf_flags = lockf.lf_flags 2562 lockf_type = lockf.lf_type 2563 if lockf_flags & 0x20: 2564 vnode_lock_output += ("{: <8s}").format('flock') 2565 if lockf_flags & 0x40: 2566 vnode_lock_output += ("{: <8s}").format('posix') 2567 if lockf_flags & 0x80: 2568 vnode_lock_output += ("{: <8s}").format('prov') 2569 if lockf_flags & 0x10: 2570 vnode_lock_output += ("{: <4s}").format('W') 2571 if lockf_flags & 0x400: 2572 vnode_lock_output += ("{: <8s}").format('ofd') 2573 else: 2574 vnode_lock_output += ("{: <4s}").format('.') 2575 2576 # POSIX file vs advisory range locks 2577 if lockf_flags & 0x40: 2578 lockf_proc = Cast(lockf.lf_id, 'proc *') 2579 vnode_lock_output += ("PID {: <18d}").format(GetProcPID(lockf_proc)) 2580 else: 2581 vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id)) 2582 2583 # lock type 2584 if lockf_type == 1: 2585 vnode_lock_output += ("{: <12s}").format('shared') 2586 else: 2587 if lockf_type == 3: 2588 vnode_lock_output += ("{: <12s}").format('exclusive') 2589 else: 2590 if lockf_type == 2: 2591 vnode_lock_output += ("{: <12s}").format('unlock') 2592 else: 2593 vnode_lock_output += ("{: <12s}").format('unknown') 2594 2595 # start and stop values 2596 vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start) 2597 vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end) 2598 return vnode_lock_output 2599 2600@header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end')) 2601def GetVnodeLocksSummary(vnode): 2602 """ Internal function to get summary of advisory locks for the given vnode 2603 params: vnode - value representing the vnode object 2604 return: str - formatted output information for the summary of advisory locks 2605 """ 2606 out_str = '' 2607 if vnode: 2608 lockf_list = vnode.v_lockf 2609 for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'): 2610 out_str += ("{: <4s}").format('H') 2611 out_str += GetVnodeLock(lockf_itr) 2612 lockf_blocker = lockf_itr.lf_blkhd.tqh_first 2613 while lockf_blocker: 2614 out_str += ("{: <4s}").format('>') 2615 out_str += GetVnodeLock(lockf_blocker) 2616 lockf_blocker = lockf_blocker.lf_block.tqe_next 2617 return out_str 2618 2619@lldb_command('showvnodelocks') 2620def ShowVnodeLocks(cmd_args=None): 2621 """ Routine to display list of advisory record locks for the given vnode address 2622 Usage: showvnodelocks <address of vnode> 2623 """ 2624 if not cmd_args: 2625 print "No arguments passed" 2626 print ShowVnodeLocks.__doc__ 2627 return False 2628 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 2629 if not vnode_val: 2630 print "unknown arguments:", str(cmd_args) 2631 return False 2632 print GetVnodeLocksSummary.header 2633 print GetVnodeLocksSummary(vnode_val) 2634 2635# EndMacro: showvnodelocks 2636 2637# Macro: showproclocks 2638 2639@lldb_command('showproclocks') 2640def ShowProcLocks(cmd_args=None): 2641 """ Routine to display list of advisory record locks for the given process 2642 Usage: showproclocks <address of proc> 2643 """ 2644 if not cmd_args: 2645 print "No arguments passed" 2646 print ShowProcLocks.__doc__ 2647 return False 2648 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *') 2649 if not proc: 2650 print "unknown arguments:", str(cmd_args) 2651 return False 2652 out_str = '' 2653 proc_filedesc = addressof(proc.p_fd) 2654 fd_ofiles = proc_filedesc.fd_ofiles 2655 seen = 0 2656 2657 for fd in xrange(0, unsigned(proc_filedesc.fd_afterlast)): 2658 if fd_ofiles[fd]: 2659 fglob = fd_ofiles[fd].fp_glob 2660 fo_type = fglob.fg_ops.fo_type 2661 if fo_type == 1: 2662 fg_data = Cast(fglob.fg_data, 'void *') 2663 fg_vnode = Cast(fg_data, 'vnode *') 2664 name = fg_vnode.v_name 2665 lockf_itr = fg_vnode.v_lockf 2666 if lockf_itr: 2667 if not seen: 2668 print GetVnodeLocksSummary.header 2669 seen = seen + 1 2670 out_str += ("\n( fd {:d}, name ").format(fd) 2671 if not name: 2672 out_str += "(null) )\n" 2673 else: 2674 out_str += "{:s} )\n".format(name) 2675 print out_str 2676 print GetVnodeLocksSummary(fg_vnode) 2677 print "\n{0: d} total locks for {1: #018x}".format(seen, proc) 2678 2679# EndMacro: showproclocks 2680 2681@lldb_type_summary(['vnode_t', 'vnode *']) 2682@header("{0: <20s} {1: >8s} {2: >9s} {3: >8s} {4: <20s} {5: <6s} {6: <20s} {7: <6s} {8: <6s} {9: <35s}".format('vnode', 'usecount', 'kusecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name')) 2683def GetVnodeSummary(vnode): 2684 """ Get a summary of important information out of vnode 2685 """ 2686 out_str = '' 2687 format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: >8d} {4: <#020x} {5: <6s} {6: <#020x} {7: <6s} {8: <6s} {9: <35s}" 2688 usecount = int(vnode.v_usecount) 2689 kusecount = int(vnode.v_kusecount) 2690 iocount = int(vnode.v_iocount) 2691 v_data_ptr = int(hex(vnode.v_data), 16) 2692 vtype = int(vnode.v_type) 2693 vtype_str = "%d" % vtype 2694 vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition 2695 if vtype >= 0 and vtype < len(vnode_types): 2696 vtype_str = vnode_types[vtype] 2697 parent_ptr = int(hex(vnode.v_parent), 16) 2698 name_ptr = int(hex(vnode.v_name), 16) 2699 name ="" 2700 if name_ptr != 0: 2701 name = str(vnode.v_name) 2702 elif int(vnode.v_tag) == 16 : 2703 cnode = Cast(vnode.v_data, 'cnode *') 2704 name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *')) 2705 mapped = '-' 2706 csblob_version = '-' 2707 if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0): 2708 csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen) 2709 # Check to see if vnode is mapped/unmapped 2710 if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0: 2711 mapped = '1' 2712 else: 2713 mapped = '0' 2714 out_str += format_string.format(vnode, usecount, kusecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name) 2715 return out_str 2716 2717@lldb_command('showallvnodes') 2718def ShowAllVnodes(cmd_args=None): 2719 """ Display info about all vnodes 2720 """ 2721 mntlist = kern.globals.mountlist 2722 print GetVnodeSummary.header 2723 for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'): 2724 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 2725 print GetVnodeSummary(vnodeval) 2726 return 2727 2728@lldb_command('showvnode') 2729def ShowVnode(cmd_args=None): 2730 """ Display info about one vnode 2731 usage: showvnode <vnode> 2732 """ 2733 if cmd_args == None or len(cmd_args) < 1: 2734 print "Please provide valid vnode argument. Type help showvnode for help." 2735 return 2736 vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *') 2737 print GetVnodeSummary.header 2738 print GetVnodeSummary(vnodeval) 2739 2740@lldb_command('showvolvnodes') 2741def ShowVolVnodes(cmd_args=None): 2742 """ Display info about all vnodes of a given mount_t 2743 """ 2744 if cmd_args == None or len(cmd_args) < 1: 2745 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help" 2746 return 2747 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t') 2748 print GetVnodeSummary.header 2749 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 2750 print GetVnodeSummary(vnodeval) 2751 return 2752 2753@lldb_command('showvolbusyvnodes') 2754def ShowVolBusyVnodes(cmd_args=None): 2755 """ Display info about busy (iocount!=0) vnodes of a given mount_t 2756 """ 2757 if cmd_args == None or len(cmd_args) < 1: 2758 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help" 2759 return 2760 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t') 2761 print GetVnodeSummary.header 2762 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'): 2763 if int(vnodeval.v_iocount) != 0: 2764 print GetVnodeSummary(vnodeval) 2765 2766@lldb_command('showallbusyvnodes') 2767def ShowAllBusyVnodes(cmd_args=None): 2768 """ Display info about all busy (iocount!=0) vnodes 2769 """ 2770 mntlistval = kern.globals.mountlist 2771 for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'): 2772 ShowVolBusyVnodes([hex(mntval)]) 2773 2774@lldb_command('print_vnode') 2775def PrintVnode(cmd_args=None): 2776 """ Prints out the fields of a vnode struct 2777 Usage: print_vnode <vnode> 2778 """ 2779 if not cmd_args: 2780 print "Please provide valid vnode argument. Type help print_vnode for help." 2781 return 2782 ShowVnode(cmd_args) 2783 2784@lldb_command('showworkqvnodes') 2785def ShowWorkqVnodes(cmd_args=None): 2786 """ Print the vnode worker list 2787 Usage: showworkqvnodes <struct mount *> 2788 """ 2789 if not cmd_args: 2790 print "Please provide valid mount argument. Type help showworkqvnodes for help." 2791 return 2792 2793 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *') 2794 vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *') 2795 print GetVnodeSummary.header 2796 while int(vp) != 0: 2797 print GetVnodeSummary(vp) 2798 vp = vp.v_mntvnodes.tqe_next 2799 2800@lldb_command('shownewvnodes') 2801def ShowNewVnodes(cmd_args=None): 2802 """ Print the new vnode list 2803 Usage: shownewvnodes <struct mount *> 2804 """ 2805 if not cmd_args: 2806 print "Please provide valid mount argument. Type help shownewvnodes for help." 2807 return 2808 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *') 2809 vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *') 2810 print GetVnodeSummary.header 2811 while int(vp) != 0: 2812 print GetVnodeSummary(vp) 2813 vp = vp.v_mntvnodes.tqe_next 2814 2815 2816@lldb_command('showprocvnodes') 2817def ShowProcVnodes(cmd_args=None): 2818 """ Routine to print out all the open fds which are vnodes in a process 2819 Usage: showprocvnodes <proc *> 2820 """ 2821 if not cmd_args: 2822 print "Please provide valid proc argument. Type help showprocvnodes for help." 2823 return 2824 procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *') 2825 fdptr = addressof(procptr.p_fd) 2826 if int(fdptr.fd_cdir) != 0: 2827 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir)) 2828 if int(fdptr.fd_rdir) != 0: 2829 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir)) 2830 count = 0 2831 print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary.header 2832 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers 2833 fpptr = Cast(fdptr.fd_ofiles, 'uint64_t *') 2834 while count < fdptr.fd_nfiles: 2835 fpp = dereference(fpptr) 2836 fproc = kern.GetValueFromAddress(int(fpp), 'fileproc *') 2837 if int(fproc) != 0: 2838 fglob = dereference(fproc).fp_glob 2839 fglob_fg_data = Cast(fglob.fg_data, 'void *') 2840 flags = "" 2841 if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1): 2842 if (fpp.fp_flags & GetEnumValue('fileproc_flags_t', 'FP_CLOEXEC')): flags += 'E' 2843 if (fpp.fp_flags & GetEnumValue('fileproc_flags_t', 'FP_CLOFORK')): flags += 'F' 2844 if (fdptr.fd_ofileflags[count] & 4): flags += 'R' 2845 if (fdptr.fd_ofileflags[count] & 8): flags += 'C' 2846 print '{0: <5d} {1: <7s} {2: <#020x} '.format(count, flags, fglob) + GetVnodeSummary(Cast(fglob_fg_data, 'vnode *')) 2847 count += 1 2848 fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'uint64_t *') 2849 2850@lldb_command('showallprocvnodes') 2851def ShowAllProcVnodes(cmd_args=None): 2852 """ Routine to print out all the open fds which are vnodes 2853 """ 2854 2855 procptr = Cast(kern.globals.allproc.lh_first, 'proc *') 2856 while procptr and int(procptr) != 0: 2857 print '{:<s}'.format("=" * 106) 2858 print GetProcInfo(procptr) 2859 ShowProcVnodes([int(procptr)]) 2860 procptr = procptr.p_list.le_next 2861 2862@xnudebug_test('test_vnode') 2863def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ): 2864 """ Test the functionality of vnode related commands 2865 returns 2866 - False on failure 2867 - True on success 2868 """ 2869 if not isConnected: 2870 print "Target is not connected. Cannot test memstats" 2871 return False 2872 res = lldb.SBCommandReturnObject() 2873 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res) 2874 result = res.GetOutput() 2875 if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5: 2876 return True 2877 else: 2878 return False 2879 2880# Macro: showallmtx 2881@lldb_type_summary(['_lck_grp_ *']) 2882def GetMutexEntry(mtxg): 2883 """ Summarize a mutex group entry with important information. 2884 params: 2885 mtxg: value - obj representing a mutex group in kernel 2886 returns: 2887 out_string - summary of the mutex group 2888 """ 2889 out_string = "" 2890 2891 if kern.ptrsize == 8: 2892 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 2893 else: 2894 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 2895 2896 if mtxg.lck_grp_mtxcnt: 2897 out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt, 2898 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt, 2899 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name) 2900 return out_string 2901 2902@lldb_command('showallmtx') 2903def ShowAllMtx(cmd_args=None): 2904 """ Routine to print a summary listing of all mutexes 2905 """ 2906 2907 if kern.ptrsize == 8: 2908 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 2909 else: 2910 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 2911 2912 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME') 2913 2914 mtxgrp_queue_head = kern.globals.lck_grp_queue 2915 mtxgrp_ptr_type = GetType('_lck_grp_ *') 2916 2917 for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"): 2918 print GetMutexEntry(mtxgrp_ptr) 2919 return 2920# EndMacro: showallmtx 2921 2922# Macro: showallrwlck 2923@lldb_type_summary(['_lck_grp_ *']) 2924def GetRWLEntry(rwlg): 2925 """ Summarize a reader writer lock group with important information. 2926 params: 2927 rwlg: value - obj representing a reader writer lock group in kernel 2928 returns: 2929 out_string - summary of the reader writer lock group 2930 """ 2931 out_string = "" 2932 2933 if kern.ptrsize == 8: 2934 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 2935 else: 2936 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} ' 2937 2938 if rwlg.lck_grp_rwcnt: 2939 out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt, 2940 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt, 2941 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name) 2942 return out_string 2943 2944#Macro: showlock 2945@lldb_type_summary(['lck_mtx_t *']) 2946@header("===== Mutex Lock Summary =====") 2947def GetMutexLockSummary(mtx): 2948 """ Summarize mutex lock with important information. 2949 params: 2950 mtx: value - obj representing a mutex lock in kernel 2951 returns: 2952 out_str - summary of the mutex lock 2953 """ 2954 if not mtx: 2955 return "Invalid lock value: 0x0" 2956 2957 if kern.arch == "x86_64": 2958 out_str = "Lock Type : MUTEX\n" 2959 if mtx.lck_mtx_tag == 0x07ff1007 : 2960 out_str += "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx.lck_mtx_ptr) 2961 mtx = Cast(mtx.lck_mtx_ptr, 'lck_mtx_t *') 2962 2963 if mtx.lck_mtx_tag == 0x07fe2007 : 2964 out_str += "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx.lck_mtx_tag) 2965 2966 out_str += "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx=mtx) 2967 out_str += "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx=mtx) 2968 out_str += "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx=mtx) 2969 out_str += "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx=mtx) 2970 out_str += "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx=mtx) 2971 out_str += "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx=mtx) 2972 out_str += "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx=mtx) 2973 out_str += "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx=mtx) 2974 if mtx.lck_mtx_pad32 == 0xFFFFFFFF : 2975 out_str += "Canary (valid) : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx) 2976 else: 2977 out_str += "Canary (INVALID) : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx) 2978 return out_str 2979 2980 LCK_MTX_TYPE = 0x22 2981 out_str = "Lock Type\t\t: MUTEX\n" 2982 if mtx.lck_mtx_type != LCK_MTX_TYPE: 2983 out_str += "Mutex Invalid" 2984 return out_str 2985 out_str += "Owner Thread\t\t: {:#x}".format(mtx.lck_mtx_data & ~0x3) 2986 if (mtx.lck_mtx_data & ~0x3) == 0xfffffff0: 2987 out_str += " Held as spinlock" 2988 out_str += "\nNumber of Waiters\t: {:d}\n".format(mtx.lck_mtx_waiters) 2989 out_str += "Flags\t\t\t: " 2990 if mtx.lck_mtx_data & 0x1: 2991 out_str += "[Interlock Locked] " 2992 if mtx.lck_mtx_data & 0x2: 2993 out_str += "[Wait Flag]" 2994 return out_str 2995 2996@lldb_type_summary(['lck_spin_t *']) 2997@header("===== SpinLock Summary =====") 2998def GetSpinLockSummary(spinlock): 2999 """ Summarize spinlock with important information. 3000 params: 3001 spinlock: value - obj representing a spinlock in kernel 3002 returns: 3003 out_str - summary of the spinlock 3004 """ 3005 if not spinlock: 3006 return "Invalid lock value: 0x0" 3007 3008 out_str = "Lock Type\t\t: SPINLOCK\n" 3009 if kern.arch == "x86_64": 3010 out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock) 3011 return out_str 3012 LCK_SPIN_TYPE = 0x11 3013 if spinlock.type != LCK_SPIN_TYPE: 3014 out_str += "Spinlock Invalid" 3015 return out_str 3016 lock_data = spinlock.hwlock.lock_data 3017 if lock_data == 1: 3018 out_str += "Invalid state: interlock is locked but no owner\n" 3019 return out_str 3020 out_str += "Owner Thread\t\t: " 3021 if lock_data == 0: 3022 out_str += "None\n" 3023 else: 3024 out_str += "{:#x}\n".format(lock_data & ~0x1) 3025 if (lock_data & 1) == 0: 3026 out_str += "Invalid state: owned but interlock bit is not set\n" 3027 return out_str 3028 3029@lldb_type_summary(['lck_rw_t *']) 3030@header("===== RWLock Summary =====") 3031def GetRWLockSummary(rwlock): 3032 """ Summarize rwlock with important information. 3033 params: 3034 rwlock: value - obj representing a lck_rw_lock in kernel 3035 returns: 3036 out_str - summary of the rwlock 3037 """ 3038 if not rwlock: 3039 return "Invalid lock value: 0x0" 3040 3041 out_str = "Lock Type\t\t: RWLOCK\n" 3042 lock_word = rwlock.word 3043 out_str += "Blocking\t\t: " 3044 if lock_word.can_sleep == 0: 3045 out_str += "FALSE\n" 3046 else: 3047 out_str += "TRUE\n" 3048 if lock_word.priv_excl == 0: 3049 out_str += "Recusive\t\t: shared recursive\n" 3050 out_str += "Interlock\t\t: {:#x}\n".format(lock_word.interlock) 3051 out_str += "Writer bits\t\t: " 3052 if lock_word.want_upgrade == 0 and lock_word.want_excl == 0: 3053 out_str += "-\n" 3054 else: 3055 if lock_word.want_upgrade == 1: 3056 out_str += "Read-to-write upgrade requested" 3057 if lock_word.want_excl == 1: 3058 out_str += "," 3059 else: 3060 out_str += "\n" 3061 if lock_word.want_excl == 1: 3062 out_str += "Write ownership requested\n" 3063 out_str += "Write owner\t\t: {:#x}\n".format(rwlock.lck_rw_owner) 3064 out_str += "Reader(s) \t\t: " 3065 if lock_word.shared_count > 0: 3066 out_str += "{:#d}\n".format(lock_word.shared_count) 3067 else: 3068 out_str += "No readers\n" 3069 if lock_word.r_waiting == 1: 3070 out_str += "Reader(s) blocked\t: TRUE\n" 3071 if lock_word.w_waiting == 1: 3072 out_str += "Writer(s) blocked\t: TRUE\n" 3073 return out_str 3074 3075@lldb_command('showlock', 'MSR') 3076def ShowLock(cmd_args=None, cmd_options={}): 3077 """ Show info about a lock - its state and owner thread details 3078 Usage: showlock <address of a lock> 3079 -M : to consider <addr> as lck_mtx_t 3080 -S : to consider <addr> as lck_spin_t 3081 -R : to consider <addr> as lck_rw_t 3082 """ 3083 if not cmd_args: 3084 raise ArgumentError("Please specify the address of the lock whose info you want to view.") 3085 return 3086 3087 summary_str = "" 3088 addr = cmd_args[0] 3089 ## from osfmk/arm/locks.h 3090 if "-M" in cmd_options: 3091 lock_mtx = kern.GetValueFromAddress(addr, 'lck_mtx_t *') 3092 summary_str = GetMutexLockSummary(lock_mtx) 3093 elif "-S" in cmd_options: 3094 lock_spin = kern.GetValueFromAddress(addr, 'lck_spin_t *') 3095 summary_str = GetSpinLockSummary(lock_spin) 3096 elif "-R" in cmd_options: 3097 lock_rw = kern.GetValueFromAddress(addr, 'lck_rw_t *') 3098 summary_str = GetRWLockSummary(lock_rw) 3099 else: 3100 summary_str = "Please specify supported lock option(-M/-S/-R)" 3101 3102 print summary_str 3103 3104#EndMacro: showlock 3105 3106def getThreadRW(thread, debug, elem_find, force_print): 3107 """ Helper routine for finding per thread rw lock: 3108 returns: 3109 String with info 3110 """ 3111 out = "" 3112 ## if we are not in debug mode do not access thread.rw_lock_held 3113 if not debug: 3114 if not force_print: 3115 if thread.rwlock_count == 0: 3116 return out 3117 out = "{:<19s} {:>19s} \n".format("Thread", "rwlock_count") 3118 out += "{:<#19x} ".format(thread) 3119 out += "{:>19d} ".format(thread.rwlock_count) 3120 return out 3121 3122 rw_locks_held = thread.rw_lock_held 3123 if not force_print: 3124 if thread.rwlock_count == 0 and rw_locks_held.rwld_locks_acquired == 0: 3125 return out 3126 3127 out = "{:<19s} {:>19s} {:>19s} {:>29s}\n".format("Thread", "rwlock_count", "rwlock_acquired", "RW_Debug_info_missing") 3128 out += "{:<#19x} ".format(thread) 3129 out += "{:>19d} ".format(thread.rwlock_count) 3130 out += "{:>19d} ".format(rw_locks_held.rwld_locks_acquired) 3131 3132 if rw_locks_held.rwld_overflow: 3133 out += "{:>29s}\n".format("TRUE") 3134 else: 3135 out += "{:>29s}\n".format("FALSE") 3136 3137 found = set() 3138 if rw_locks_held.rwld_locks_saved > 0: 3139 lock_entry = rw_locks_held.rwld_locks 3140 num_entry = sizeof(lock_entry) / sizeof(lock_entry[0]) 3141 out += "{:>10s} {:<19s} {:>10s} {:>10s} {:>10s} {:<19s}\n".format(" ", "Lock", "Write", "Read", " ", "Caller") 3142 for i in range(num_entry): 3143 entry = lock_entry[i] 3144 if entry.rwlde_lock: 3145 out += "{:>10s} ".format(" ") 3146 found.add(hex(entry.rwlde_lock)) 3147 out += "{:<#19x} ".format(entry.rwlde_lock) 3148 write = 0 3149 read = 0 3150 if entry.rwlde_mode_count < 0: 3151 write = 1 3152 if entry.rwlde_mode_count > 0: 3153 read = entry.rwlde_mode_count 3154 out += "{:>10d} ".format(write) 3155 out += "{:>10d} ".format(read) 3156 out += "{:>10s} ".format(" ") 3157 caller = vm_unpack_pointer(entry.rwlde_caller_packed, kern.globals.rwlde_caller_packing_params, 'void *') 3158 out += "{:<#19x}\n".format(caller) 3159 3160 if elem_find != 0: 3161 if elem_find in found: 3162 return out 3163 else: 3164 return "" 3165 else: 3166 return out 3167 3168def rwLockDebugDisabled(): 3169 ## disLkRWDebug 0x00000010 from locks.h 3170 if (kern.globals.LcksOpts and 0x00000010) == 0x00000010: 3171 return True 3172 else: 3173 return False 3174 3175@lldb_command('showthreadrwlck') 3176def ShowThreadRWLck(cmd_args = None): 3177 """ Routine to print a best effort summary of rwlocks held 3178 """ 3179 if not cmd_args: 3180 raise ArgumentError("Please specify the thread pointer") 3181 return 3182 thread = kern.GetValueFromAddress(cmd_args[0], 'thread_t') 3183 if not thread: 3184 raise ArgumentError("Invalid thread pointer") 3185 return 3186 3187 debug = True 3188 if rwLockDebugDisabled(): 3189 print "WARNING: Best effort per-thread rwlock tracking is OFF\n" 3190 debug = False 3191 3192 string = getThreadRW(thread, debug, 0, True) 3193 print string 3194 3195 return 3196# EndMacro: showthreadrwlck 3197 3198@lldb_command('showallrwlckheld') 3199def ShowAllRWLckHeld(cmd_args = None): 3200 """ Routine to print a summary listing of all read/writer locks 3201 tracked per thread 3202 """ 3203 debug = True 3204 if rwLockDebugDisabled(): 3205 print "WARNING: Best effort per-thread rwlock tracking is OFF\n" 3206 debug = False 3207 3208 for t in kern.tasks: 3209 for th in IterateQueue(t.threads, 'thread *', 'task_threads'): 3210 print getThreadRW(th, debug, 0, False) 3211 3212 return 3213# EndMacro: showallrwlckheld 3214 3215@lldb_command('tryfindrwlckholders') 3216def tryFindRwlckHolders(cmd_args = None): 3217 """ Best effort routing to find the current holders of 3218 a rwlock 3219 """ 3220 if not cmd_args: 3221 raise ArgumentError("Please specify a rw_lock_t pointer") 3222 return 3223 3224 if rwLockDebugDisabled(): 3225 print "WARNING: Best effort per-thread rwlock tracking is OFF\n" 3226 return 3227 3228 print "This is a best effort mechanism, if threads have lock info missing we might not be able to find the lock.\n" 3229 rw_to_find = cmd_args[0] 3230 for t in kern.tasks: 3231 for th in IterateQueue(t.threads, 'thread *', 'task_threads'): 3232 print getThreadRW(th, True, rw_to_find, False) 3233 3234 return 3235# EndMacro: tryfindrwlckholders 3236 3237@lldb_command('showallrwlck') 3238def ShowAllRWLck(cmd_args=None): 3239 """ Routine to print a summary listing of all read/writer locks 3240 """ 3241 if kern.ptrsize == 8: 3242 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 3243 else: 3244 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} ' 3245 3246 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME') 3247 3248 rwlgrp_queue_head = kern.globals.lck_grp_queue 3249 rwlgrp_ptr_type = GetType('_lck_grp_ *') 3250 for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"): 3251 print GetRWLEntry(rwlgrp_ptr) 3252 return 3253# EndMacro: showallrwlck 3254 3255#Macro: showbootermemorymap 3256@lldb_command('showbootermemorymap') 3257def ShowBooterMemoryMap(cmd_args=None): 3258 """ Prints out the phys memory map from kernelBootArgs 3259 Supported only on x86_64 3260 """ 3261 if kern.arch != 'x86_64': 3262 print "showbootermemorymap not supported on this architecture" 3263 return 3264 3265 out_string = "" 3266 3267 # Memory type map 3268 memtype_dict = { 3269 0: 'Reserved', 3270 1: 'LoaderCode', 3271 2: 'LoaderData', 3272 3: 'BS_code', 3273 4: 'BS_data', 3274 5: 'RT_code', 3275 6: 'RT_data', 3276 7: 'Convention', 3277 8: 'Unusable', 3278 9: 'ACPI_recl', 3279 10: 'ACPI_NVS', 3280 11: 'MemMapIO', 3281 12: 'MemPortIO', 3282 13: 'PAL_code' 3283 } 3284 3285 boot_args = kern.globals.kernelBootArgs 3286 msize = boot_args.MemoryMapDescriptorSize 3287 mcount = (boot_args.MemoryMapSize) / unsigned(msize) 3288 3289 out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes") 3290 3291 i = 0 3292 while i < mcount: 3293 mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + kern.VM_MIN_KERNEL_ADDRESS + unsigned(i*msize), 'EfiMemoryRange *') 3294 mtype = unsigned(mptr.Type) 3295 if mtype in memtype_dict: 3296 out_string += "{0: <12s}".format(memtype_dict[mtype]) 3297 else: 3298 out_string += "{0: <12s}".format("UNKNOWN") 3299 3300 if mptr.VirtualStart == 0: 3301 out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute) 3302 else: 3303 out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute) 3304 i = i + 1 3305 3306 print out_string 3307#EndMacro: showbootermemorymap 3308 3309@lldb_command('show_all_purgeable_objects') 3310def ShowAllPurgeableVmObjects(cmd_args=None): 3311 """ Routine to print a summary listing of all the purgeable vm objects 3312 """ 3313 print "\n-------------------- VOLATILE OBJECTS --------------------\n" 3314 ShowAllPurgeableVolatileVmObjects() 3315 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n" 3316 ShowAllPurgeableNonVolatileVmObjects() 3317 3318@lldb_command('show_all_purgeable_nonvolatile_objects') 3319def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None): 3320 """ Routine to print a summary listing of all the vm objects in 3321 the purgeable_nonvolatile_queue 3322 """ 3323 3324 nonvolatile_total = lambda:None 3325 nonvolatile_total.objects = 0 3326 nonvolatile_total.vsize = 0 3327 nonvolatile_total.rsize = 0 3328 nonvolatile_total.wsize = 0 3329 nonvolatile_total.csize = 0 3330 nonvolatile_total.disowned_objects = 0 3331 nonvolatile_total.disowned_vsize = 0 3332 nonvolatile_total.disowned_rsize = 0 3333 nonvolatile_total.disowned_wsize = 0 3334 nonvolatile_total.disowned_csize = 0 3335 3336 queue_len = kern.globals.purgeable_nonvolatile_count 3337 queue_head = kern.globals.purgeable_nonvolatile_queue 3338 3339 print 'purgeable_nonvolatile_queue:{: <#018x} purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len) 3340 print 'N:non-volatile V:volatile E:empty D:deny\n' 3341 3342 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process") 3343 idx = 0 3344 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'): 3345 idx += 1 3346 ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total) 3347 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.disowned_objects, nonvolatile_total.disowned_vsize, nonvolatile_total.disowned_rsize, nonvolatile_total.disowned_wsize, nonvolatile_total.disowned_csize) 3348 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.objects, nonvolatile_total.vsize, nonvolatile_total.rsize, nonvolatile_total.wsize, nonvolatile_total.csize) 3349 3350 3351def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total): 3352 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue 3353 params: 3354 object - core.value : a object of type 'struct vm_object *' 3355 returns: 3356 None 3357 """ 3358 page_size = kern.globals.page_size 3359 if object.purgable == 0: 3360 purgable = "N" 3361 elif object.purgable == 1: 3362 purgable = "V" 3363 elif object.purgable == 2: 3364 purgable = "E" 3365 elif object.purgable == 3: 3366 purgable = "D" 3367 else: 3368 purgable = "?" 3369 if object.pager == 0: 3370 compressed_count = 0 3371 else: 3372 compressor_pager = Cast(object.pager, 'compressor_pager *') 3373 compressed_count = compressor_pager.cpgr_num_slots_occupied 3374 3375 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner)) 3376 3377 nonvolatile_total.objects += 1 3378 nonvolatile_total.vsize += object.vo_un1.vou_size/page_size 3379 nonvolatile_total.rsize += object.resident_page_count 3380 nonvolatile_total.wsize += object.wired_page_count 3381 nonvolatile_total.csize += compressed_count 3382 if object.vo_un2.vou_owner == 0: 3383 nonvolatile_total.disowned_objects += 1 3384 nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size 3385 nonvolatile_total.disowned_rsize += object.resident_page_count 3386 nonvolatile_total.disowned_wsize += object.wired_page_count 3387 nonvolatile_total.disowned_csize += compressed_count 3388 3389 3390@lldb_command('show_all_purgeable_volatile_objects') 3391def ShowAllPurgeableVolatileVmObjects(cmd_args=None): 3392 """ Routine to print a summary listing of all the vm objects in 3393 the purgeable queues 3394 """ 3395 volatile_total = lambda:None 3396 volatile_total.objects = 0 3397 volatile_total.vsize = 0 3398 volatile_total.rsize = 0 3399 volatile_total.wsize = 0 3400 volatile_total.csize = 0 3401 volatile_total.disowned_objects = 0 3402 volatile_total.disowned_vsize = 0 3403 volatile_total.disowned_rsize = 0 3404 volatile_total.disowned_wsize = 0 3405 volatile_total.disowned_csize = 0 3406 3407 purgeable_queues = kern.globals.purgeable_queues 3408 print "---------- OBSOLETE\n" 3409 ShowPurgeableQueue(purgeable_queues[0], volatile_total) 3410 print "\n\n---------- FIFO\n" 3411 ShowPurgeableQueue(purgeable_queues[1], volatile_total) 3412 print "\n\n---------- LIFO\n" 3413 ShowPurgeableQueue(purgeable_queues[2], volatile_total) 3414 3415 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.disowned_objects, volatile_total.disowned_vsize, volatile_total.disowned_rsize, volatile_total.disowned_wsize, volatile_total.disowned_csize) 3416 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.objects, volatile_total.vsize, volatile_total.rsize, volatile_total.wsize, volatile_total.csize) 3417 purgeable_count = kern.globals.vm_page_purgeable_count 3418 purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count 3419 if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize: 3420 mismatch = "<--------- MISMATCH\n" 3421 else: 3422 mismatch = "" 3423 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch) 3424 3425 3426def ShowPurgeableQueue(qhead, volatile_total): 3427 print "----- GROUP 0\n" 3428 ShowPurgeableGroup(qhead.objq[0], volatile_total) 3429 print "----- GROUP 1\n" 3430 ShowPurgeableGroup(qhead.objq[1], volatile_total) 3431 print "----- GROUP 2\n" 3432 ShowPurgeableGroup(qhead.objq[2], volatile_total) 3433 print "----- GROUP 3\n" 3434 ShowPurgeableGroup(qhead.objq[3], volatile_total) 3435 print "----- GROUP 4\n" 3436 ShowPurgeableGroup(qhead.objq[4], volatile_total) 3437 print "----- GROUP 5\n" 3438 ShowPurgeableGroup(qhead.objq[5], volatile_total) 3439 print "----- GROUP 6\n" 3440 ShowPurgeableGroup(qhead.objq[6], volatile_total) 3441 print "----- GROUP 7\n" 3442 ShowPurgeableGroup(qhead.objq[7], volatile_total) 3443 3444def ShowPurgeableGroup(qhead, volatile_total): 3445 idx = 0 3446 for object in IterateQueue(qhead, 'struct vm_object *', 'objq'): 3447 if idx == 0: 3448# print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s} {:18s} {:>6s} {:<20s} {:s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process","volatilizer","pid","process","") 3449 print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process") 3450 idx += 1 3451 ShowPurgeableVolatileVmObject(object, idx, volatile_total) 3452 3453def ShowPurgeableVolatileVmObject(object, idx, volatile_total): 3454 """ Routine to print out a summary a VM object in a purgeable queue 3455 params: 3456 object - core.value : a object of type 'struct vm_object *' 3457 returns: 3458 None 3459 """ 3460## if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer): 3461# diff=" !=" 3462## else: 3463# diff=" " 3464 page_size = kern.globals.page_size 3465 if object.purgable == 0: 3466 purgable = "N" 3467 elif object.purgable == 1: 3468 purgable = "V" 3469 elif object.purgable == 2: 3470 purgable = "E" 3471 elif object.purgable == 3: 3472 purgable = "D" 3473 else: 3474 purgable = "?" 3475 if object.pager == 0: 3476 compressed_count = 0 3477 else: 3478 compressor_pager = Cast(object.pager, 'compressor_pager *') 3479 compressed_count = compressor_pager.cpgr_num_slots_occupied 3480# print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {: <#018x} {:>6d} {:<20s} {: <#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner),object.vo_purgeable_volatilizer,GetProcPIDForObjectOwner(object.vo_purgeable_volatilizer),GetProcNameForObjectOwner(object.vo_purgeable_volatilizer),diff) 3481 print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner)) 3482 volatile_total.objects += 1 3483 volatile_total.vsize += object.vo_un1.vou_size/page_size 3484 volatile_total.rsize += object.resident_page_count 3485 volatile_total.wsize += object.wired_page_count 3486 volatile_total.csize += compressed_count 3487 if object.vo_un2.vou_owner == 0: 3488 volatile_total.disowned_objects += 1 3489 volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size 3490 volatile_total.disowned_rsize += object.resident_page_count 3491 volatile_total.disowned_wsize += object.wired_page_count 3492 volatile_total.disowned_csize += compressed_count 3493 3494 3495def GetCompressedPagesForObject(obj): 3496 """Stuff 3497 """ 3498 pager = Cast(obj.pager, 'compressor_pager_t') 3499 return pager.cpgr_num_slots_occupied 3500 """ # commented code below 3501 if pager.cpgr_num_slots > 128: 3502 slots_arr = pager.cpgr_slots.cpgr_islots 3503 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128 3504 index = 0 3505 compressor_slot = 0 3506 compressed_pages = 0 3507 while index < num_indirect_slot_ptr: 3508 compressor_slot = 0 3509 if slots_arr[index]: 3510 while compressor_slot < 128: 3511 if slots_arr[index][compressor_slot]: 3512 compressed_pages += 1 3513 compressor_slot += 1 3514 index += 1 3515 else: 3516 slots_arr = pager.cpgr_slots.cpgr_dslots 3517 compressor_slot = 0 3518 compressed_pages = 0 3519 while compressor_slot < pager.cpgr_num_slots: 3520 if slots_arr[compressor_slot]: 3521 compressed_pages += 1 3522 compressor_slot += 1 3523 return compressed_pages 3524 """ 3525 3526def ShowTaskVMEntries(task, show_pager_info, show_all_shadows): 3527 """ Routine to print out a summary listing of all the entries in a vm_map 3528 params: 3529 task - core.value : a object of type 'task *' 3530 returns: 3531 None 3532 """ 3533 print "vm_map entries for task " + hex(task) 3534 print GetTaskSummary.header 3535 print GetTaskSummary(task) 3536 if not task.map: 3537 print "Task {0: <#020x} has map = 0x0" 3538 return None 3539 showmapvme(task.map, 0, 0, show_pager_info, show_all_shadows, False) 3540 3541@lldb_command("showmapvme", "A:B:F:PRST") 3542def ShowMapVME(cmd_args=None, cmd_options={}): 3543 """Routine to print out info about the specified vm_map and its vm entries 3544 usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P] 3545 Use -A <start> flag to start at virtual address <start> 3546 Use -B <end> flag to end at virtual address <end> 3547 Use -F <virtaddr> flag to find just the VME containing the given VA 3548 Use -S flag to show VM object shadow chains 3549 Use -P flag to show pager info (mapped file, compressed pages, ...) 3550 Use -R flag to reverse order 3551 Use -T to show red-black tree pointers 3552 """ 3553 if cmd_args == None or len(cmd_args) < 1: 3554 print "Invalid argument.", ShowMapVME.__doc__ 3555 return 3556 show_pager_info = False 3557 show_all_shadows = False 3558 show_rb_tree = False 3559 start_vaddr = 0 3560 end_vaddr = 0 3561 reverse_order = False 3562 if "-A" in cmd_options: 3563 start_vaddr = unsigned(int(cmd_options['-A'], 16)) 3564 if "-B" in cmd_options: 3565 end_vaddr = unsigned(int(cmd_options['-B'], 16)) 3566 if "-F" in cmd_options: 3567 start_vaddr = unsigned(int(cmd_options['-F'], 16)) 3568 end_vaddr = start_vaddr 3569 if "-P" in cmd_options: 3570 show_pager_info = True 3571 if "-S" in cmd_options: 3572 show_all_shadows = True 3573 if "-R" in cmd_options: 3574 reverse_order = True 3575 if "-T" in cmd_options: 3576 show_rb_tree = True 3577 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 3578 showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree) 3579 3580@lldb_command("showmapcopyvme", "A:B:F:PRST") 3581def ShowMapCopyVME(cmd_args=None, cmd_options={}): 3582 """Routine to print out info about the specified vm_map_copy and its vm entries 3583 usage: showmapcopyvme <vm_map_copy> [-A start] [-B end] [-S] [-P] 3584 Use -A <start> flag to start at virtual address <start> 3585 Use -B <end> flag to end at virtual address <end> 3586 Use -F <virtaddr> flag to find just the VME containing the given VA 3587 Use -S flag to show VM object shadow chains 3588 Use -P flag to show pager info (mapped file, compressed pages, ...) 3589 Use -R flag to reverse order 3590 Use -T to show red-black tree pointers 3591 """ 3592 if cmd_args == None or len(cmd_args) < 1: 3593 print "Invalid argument.", ShowMapVME.__doc__ 3594 return 3595 show_pager_info = False 3596 show_all_shadows = False 3597 show_rb_tree = False 3598 start_vaddr = 0 3599 end_vaddr = 0 3600 reverse_order = False 3601 if "-A" in cmd_options: 3602 start_vaddr = unsigned(int(cmd_options['-A'], 16)) 3603 if "-B" in cmd_options: 3604 end_vaddr = unsigned(int(cmd_options['-B'], 16)) 3605 if "-F" in cmd_options: 3606 start_vaddr = unsigned(int(cmd_options['-F'], 16)) 3607 end_vaddr = start_vaddr 3608 if "-P" in cmd_options: 3609 show_pager_info = True 3610 if "-S" in cmd_options: 3611 show_all_shadows = True 3612 if "-R" in cmd_options: 3613 reverse_order = True 3614 if "-T" in cmd_options: 3615 show_rb_tree = True 3616 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_copy_t') 3617 showmapcopyvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree) 3618 3619@lldb_command("showvmobject", "A:B:PRST") 3620def ShowVMObject(cmd_args=None, cmd_options={}): 3621 """Routine to print out a VM object and its shadow chain 3622 usage: showvmobject <vm_object> [-S] [-P] 3623 -S: show VM object shadow chain 3624 -P: show pager info (mapped file, compressed pages, ...) 3625 """ 3626 if cmd_args == None or len(cmd_args) < 1: 3627 print "Invalid argument.", ShowMapVME.__doc__ 3628 return 3629 show_pager_info = False 3630 show_all_shadows = False 3631 if "-P" in cmd_options: 3632 show_pager_info = True 3633 if "-S" in cmd_options: 3634 show_all_shadows = True 3635 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 3636 showvmobject(object, 0, 0, show_pager_info, show_all_shadows) 3637 3638def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False): 3639 page_size = kern.globals.page_size 3640 vnode_pager_ops = kern.globals.vnode_pager_ops 3641 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops)) 3642 depth = 0 3643 if size == 0 and object != 0 and object.internal: 3644 size = object.vo_un1.vou_size 3645 while object != 0: 3646 depth += 1 3647 if show_all_shadows == False and depth != 1 and object.shadow != 0: 3648 offset += unsigned(object.vo_un2.vou_shadow_offset) 3649 object = object.shadow 3650 continue 3651 if object.copy_strategy == 0: 3652 copy_strategy="N" 3653 elif object.copy_strategy == 2: 3654 copy_strategy="D" 3655 elif object.copy_strategy == 4: 3656 copy_strategy="S" 3657 3658 else: 3659 copy_strategy=str(object.copy_strategy) 3660 if object.internal: 3661 internal = "internal" 3662 else: 3663 internal = "external" 3664 purgeable = "NVED"[int(object.purgable)] 3665 pager_string = "" 3666 if object.phys_contiguous: 3667 pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size)) 3668 pager = object.pager 3669 if show_pager_info and pager != 0: 3670 if object.internal: 3671 pager_string = pager_string + "-> compressed:{:d}".format(GetCompressedPagesForObject(object)) 3672 elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr: 3673 vnode_pager = Cast(pager,'vnode_pager *') 3674 pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle) 3675 else: 3676 pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager) 3677 print "{:>18d} {:#018x}:{:#018x} {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} purg:{:1s} {:s} wtag:{:d} ({:d} {:d} {:d}) {:s}".format(depth,offset,offset+size,object,object.ref_count,object.true_share,copy_strategy,purgeable,internal,object.wire_tag,unsigned(object.vo_un1.vou_size)/page_size,object.resident_page_count,object.wired_page_count,pager_string) 3678# print " #{:<5d} obj {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} size:{:<10d} wired:{:<10d} resident:{:<10d} reusable:{:<10d}".format(depth,object,object.ref_count,object.true_share,copy_strategy,internal,object.vo_un1.vou_size/page_size,object.wired_page_count,object.resident_page_count,object.reusable_page_count) 3679 offset += unsigned(object.vo_un2.vou_shadow_offset) 3680 object = object.shadow 3681 3682def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order=False, show_rb_tree=False): 3683 rsize = GetResidentPageCount(map) 3684 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift") 3685 print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end,map.hdr.page_shift) 3686 showmaphdrvme(map.hdr, map.pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree) 3687 3688def showmapcopyvme(mapcopy, start_vaddr=0, end_vaddr=0, show_pager_info=True, show_all_shadows=True, reverse_order=False, show_rb_tree=False): 3689 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift") 3690 print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(mapcopy,mapcopy.offset,mapcopy.size,mapcopy.c_u.hdr.nentries,0,mapcopy.c_u.hdr.links.start,mapcopy.c_u.hdr.links.end,mapcopy.c_u.hdr.page_shift) 3691 showmaphdrvme(mapcopy.c_u.hdr, 0, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree) 3692 3693def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree): 3694 page_size = kern.globals.page_size 3695 vnode_pager_ops = kern.globals.vnode_pager_ops 3696 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops)) 3697 if hasattr(kern.globals, 'compressor_object'): 3698 compressor_object = kern.globals.compressor_object 3699 else: 3700 compressor_object = -1; 3701 vme_list_head = maphdr.links 3702 vme_ptr_type = GetType('vm_map_entry *') 3703 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset") 3704 last_end = unsigned(maphdr.links.start) 3705 skipped_entries = 0 3706 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order): 3707 if start_vaddr != 0 and end_vaddr != 0: 3708 if unsigned(vme.links.start) > end_vaddr: 3709 break 3710 if unsigned(vme.links.end) <= start_vaddr: 3711 last_end = unsigned(vme.links.end) 3712 skipped_entries = skipped_entries + 1 3713 continue 3714 if skipped_entries != 0: 3715 print "... skipped {:d} entries ...".format(skipped_entries) 3716 skipped_entries = 0 3717 if unsigned(vme.links.start) != last_end: 3718 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size) 3719 last_end = unsigned(vme.links.end) 3720 size = unsigned(vme.links.end) - unsigned(vme.links.start) 3721 object = vme.vme_object.vmo_object 3722 if object == 0: 3723 object_str = "{: <#018x}".format(object) 3724 elif vme.is_sub_map: 3725 if object == kern.globals.bufferhdr_map: 3726 object_str = "BUFFERHDR_MAP" 3727 elif object == kern.globals.mb_map: 3728 object_str = "MB_MAP" 3729 elif object == kern.globals.bsd_pageable_map: 3730 object_str = "BSD_PAGEABLE_MAP" 3731 elif object == kern.globals.ipc_kernel_map: 3732 object_str = "IPC_KERNEL_MAP" 3733 elif object == kern.globals.ipc_kernel_copy_map: 3734 object_str = "IPC_KERNEL_COPY_MAP" 3735 elif object == kern.globals.kalloc_large_map: 3736 object_str = "KALLOC_LARGE_MAP" 3737 elif object == kern.globals.kalloc_large_data_map: 3738 object_str = "KALLOC_LARGE_DATA_MAP" 3739 elif object == kern.globals.kernel_data_map: 3740 object_str = "KERNEL_DATA_MAP" 3741 elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map: 3742 object_str = "COMPRESSOR_MAP" 3743 elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map: 3744 object_str = "GZALLOC_MAP" 3745 elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map: 3746 object_str = "G_KEXT_MAP" 3747 elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap: 3748 object_str = "VECTOR_UPL_SUBMAP" 3749 else: 3750 object_str = "submap:{: <#018x}".format(object) 3751 else: 3752 if object == kern.globals.kernel_object: 3753 object_str = "KERNEL_OBJECT" 3754 elif object == kern.globals.vm_submap_object: 3755 object_str = "VM_SUBMAP_OBJECT" 3756 elif object == compressor_object: 3757 object_str = "COMPRESSOR_OBJECT" 3758 else: 3759 object_str = "{: <#018x}".format(object) 3760 offset = unsigned(vme.vme_offset) & ~0xFFF 3761 tag = unsigned(vme.vme_offset & 0xFFF) 3762 protection = "" 3763 if vme.protection & 0x1: 3764 protection +="r" 3765 else: 3766 protection += "-" 3767 if vme.protection & 0x2: 3768 protection += "w" 3769 else: 3770 protection += "-" 3771 if vme.protection & 0x4: 3772 protection += "x" 3773 else: 3774 protection += "-" 3775 max_protection = "" 3776 if vme.max_protection & 0x1: 3777 max_protection +="r" 3778 else: 3779 max_protection += "-" 3780 if vme.max_protection & 0x2: 3781 max_protection += "w" 3782 else: 3783 max_protection += "-" 3784 if vme.max_protection & 0x4: 3785 max_protection += "x" 3786 else: 3787 max_protection += "-" 3788 vme_flags = "" 3789 if vme.is_sub_map: 3790 vme_flags += "s" 3791 if vme.needs_copy: 3792 vme_flags += "n" 3793 if vme.use_pmap: 3794 vme_flags += "p" 3795 if vme.wired_count: 3796 vme_flags += "w" 3797 if vme.used_for_jit: 3798 vme_flags += "j" 3799 tagstr = "" 3800 if pmap == kern.globals.kernel_pmap: 3801 xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *') 3802 if xsite and xsite.site.flags & 0x0200: 3803 tagstr = ".{:<3d}".format(xsite.loadTag) 3804 rb_info = "" 3805 if show_rb_tree: 3806 rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent) 3807 print "{: <#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:3s}/{:3s}/{:<8s} {:<18s} {:<#18x} {:s}".format(vme,vme.links.start,vme.links.end,(unsigned(vme.links.end)-unsigned(vme.links.start))/page_size,tag,tagstr,protection,max_protection,vme_flags,object_str,offset, rb_info) 3808 if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0: 3809 object = vme.vme_object.vmo_object 3810 else: 3811 object = 0 3812 showvmobject(object, offset, size, show_pager_info, show_all_shadows) 3813 if start_vaddr != 0 or end_vaddr != 0: 3814 print "..." 3815 elif unsigned(maphdr.links.end) > last_end: 3816 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end)/page_size) 3817 return None 3818 3819def CountMapTags(map, tagcounts, slow): 3820 page_size = unsigned(kern.globals.page_size) 3821 vme_list_head = map.hdr.links 3822 vme_ptr_type = GetType('vm_map_entry *') 3823 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 3824 object = vme.vme_object.vmo_object 3825 tag = vme.vme_offset & 0xFFF 3826 if object == kern.globals.kernel_object: 3827 count = 0 3828 if not slow: 3829 count = unsigned(vme.links.end - vme.links.start) / page_size 3830 else: 3831 addr = unsigned(vme.links.start) 3832 while addr < unsigned(vme.links.end): 3833 hash_id = _calc_vm_page_hash(object, addr) 3834 page_list = kern.globals.vm_page_buckets[hash_id].page_list 3835 page = _vm_page_unpack_ptr(page_list) 3836 while (page != 0): 3837 vmpage = kern.GetValueFromAddress(page, 'vm_page_t') 3838 if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))): 3839 if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0): 3840 count += 1 3841 break 3842 page = _vm_page_unpack_ptr(vmpage.vmp_next_m) 3843 addr += page_size 3844 tagcounts[tag] += count 3845 elif vme.is_sub_map: 3846 CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow) 3847 return None 3848 3849def CountWiredObject(object, tagcounts): 3850 tagcounts[unsigned(object.wire_tag)] += object.wired_page_count 3851 return None 3852 3853def GetKmodIDName(kmod_id): 3854 kmod_val = kern.globals.kmod 3855 for kmod in IterateLinkedList(kmod_val, 'next'): 3856 if (kmod.id == kmod_id): 3857 return "{:<50s}".format(kmod.name) 3858 return "??" 3859 3860FixedTags = { 3861 0: "VM_KERN_MEMORY_NONE", 3862 1: "VM_KERN_MEMORY_OSFMK", 3863 2: "VM_KERN_MEMORY_BSD", 3864 3: "VM_KERN_MEMORY_IOKIT", 3865 4: "VM_KERN_MEMORY_LIBKERN", 3866 5: "VM_KERN_MEMORY_OSKEXT", 3867 6: "VM_KERN_MEMORY_KEXT", 3868 7: "VM_KERN_MEMORY_IPC", 3869 8: "VM_KERN_MEMORY_STACK", 3870 9: "VM_KERN_MEMORY_CPU", 3871 10: "VM_KERN_MEMORY_PMAP", 3872 11: "VM_KERN_MEMORY_PTE", 3873 12: "VM_KERN_MEMORY_ZONE", 3874 13: "VM_KERN_MEMORY_KALLOC", 3875 14: "VM_KERN_MEMORY_COMPRESSOR", 3876 15: "VM_KERN_MEMORY_COMPRESSED_DATA", 3877 16: "VM_KERN_MEMORY_PHANTOM_CACHE", 3878 17: "VM_KERN_MEMORY_WAITQ", 3879 18: "VM_KERN_MEMORY_DIAG", 3880 19: "VM_KERN_MEMORY_LOG", 3881 20: "VM_KERN_MEMORY_FILE", 3882 21: "VM_KERN_MEMORY_MBUF", 3883 22: "VM_KERN_MEMORY_UBC", 3884 23: "VM_KERN_MEMORY_SECURITY", 3885 24: "VM_KERN_MEMORY_MLOCK", 3886 25: "VM_KERN_MEMORY_REASON", 3887 26: "VM_KERN_MEMORY_SKYWALK", 3888 27: "VM_KERN_MEMORY_LTABLE", 3889 28: "VM_KERN_MEMORY_HV", 3890 29: "VM_KERN_MEMORY_KALLOC_DATA", 3891 30: "VM_KERN_MEMORY_RETIRED", 3892 31: "VM_KERN_MEMORY_KALLOC_TYPE", 3893 32: "VM_KERN_MEMORY_TRIAGE", 3894 255:"VM_KERN_MEMORY_ANY", 3895} 3896 3897def GetVMKernName(tag): 3898 """ returns the formatted name for a vmtag and 3899 the sub-tag for kmod tags. 3900 """ 3901 if ((tag <= 27) or (tag == 255)): 3902 return (FixedTags[tag], "") 3903 site = kern.globals.vm_allocation_sites[tag] 3904 if site: 3905 if site.flags & 0x007F: 3906 cstr = addressof(site.subtotals[site.subtotalscount]) 3907 return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "") 3908 else: 3909 if site.flags & 0x0200: 3910 xsite = Cast(site,'OSKextAccount *') 3911 tagstr = ".{:<3d}".format(xsite.loadTag) 3912 return (GetKmodIDName(xsite.loadTag), tagstr); 3913 else: 3914 return (kern.Symbolicate(site), "") 3915 return ("", "") 3916 3917@lldb_command("showvmtags", "ASJO") 3918def showvmtags(cmd_args=None, cmd_options={}): 3919 """Routine to print out info about kernel wired page allocations 3920 usage: showvmtags 3921 iterates kernel map and vm objects totaling allocations by tag. 3922 usage: showvmtags -S [-O] 3923 also iterates kernel object pages individually - slow. 3924 usage: showvmtags -A [-O] 3925 show all tags, even tags that have no wired count 3926 usage: showvmtags -J [-O] 3927 Output json 3928 3929 -O: list in increasing size order 3930 """ 3931 slow = False 3932 print_json = False 3933 if "-S" in cmd_options: 3934 slow = True 3935 all_tags = False 3936 if "-A" in cmd_options: 3937 all_tags = True 3938 if "-J" in cmd_options: 3939 print_json = True 3940 3941 page_size = unsigned(kern.globals.page_size) 3942 nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1 3943 tagcounts = [0] * nsites 3944 tagmapped = [0] * nsites 3945 3946 if kern.globals.vm_tag_active_update: 3947 for tag in range(nsites): 3948 site = kern.globals.vm_allocation_sites[tag] 3949 if site: 3950 tagcounts[tag] = unsigned(site.total) 3951 tagmapped[tag] = unsigned(site.mapped) 3952 else: 3953 queue_head = kern.globals.vm_objects_wired 3954 for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'): 3955 if object != kern.globals.kernel_object: 3956 CountWiredObject(object, tagcounts) 3957 3958 CountMapTags(kern.globals.kernel_map, tagcounts, slow) 3959 3960 total = 0 3961 totalmapped = 0 3962 tags = [] 3963 for tag in range(nsites): 3964 if all_tags or tagcounts[tag] or tagmapped[tag]: 3965 current = {} 3966 total += tagcounts[tag] 3967 totalmapped += tagmapped[tag] 3968 (sitestr, tagstr) = GetVMKernName(tag) 3969 current["name"] = sitestr 3970 current["size"] = tagcounts[tag] 3971 current["mapped"] = tagmapped[tag] 3972 current["tag"] = tag 3973 current["tagstr"] = tagstr 3974 current["subtotals"] = [] 3975 3976 site = kern.globals.vm_allocation_sites[tag] 3977 for sub in range(site.subtotalscount): 3978 alloctag = unsigned(site.subtotals[sub].tag) 3979 amount = unsigned(site.subtotals[sub].total) 3980 subsite = kern.globals.vm_allocation_sites[alloctag] 3981 if alloctag and subsite: 3982 (sitestr, tagstr) = GetVMKernName(alloctag) 3983 current["subtotals"].append({ 3984 "amount": amount, 3985 "flags": int(subsite.flags), 3986 "tag": alloctag, 3987 "tagstr": tagstr, 3988 "sitestr": sitestr, 3989 }) 3990 tags.append(current) 3991 3992 if "-O" in cmd_options: 3993 tags.sort(key = lambda tag: tag['size']) 3994 3995 # Serializing to json here ensure we always catch bugs preventing 3996 # serialization 3997 as_json = json.dumps(tags) 3998 if print_json: 3999 print as_json 4000 else: 4001 print " vm_allocation_tag_highest: {:<7d} ".format(nsites - 1) 4002 print " {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "size", "mapped", "name") 4003 for tag in tags: 4004 if not tagstr: 4005 tagstr = "" 4006 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag["tag"], tag["tagstr"], tag["size"] / 1024, tag["mapped"] / 1024, tag["name"]) 4007 for sub in tag["subtotals"]: 4008 if ((sub["flags"] & 0x007f) == 0): 4009 kind_str = "named" 4010 else: 4011 kind_str = "from" 4012 4013 print " {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", sub["amount"] / 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"]) 4014 4015 print "Total: {:>7d}K {:>7d}K".format(total / 1024, totalmapped / 1024) 4016 return None 4017 4018 4019def FindVMEntriesForVnode(task, vn): 4020 """ returns an array of vme that have the vnode set to defined vnode 4021 each entry in array is of format (vme, start_addr, end_address, protection) 4022 """ 4023 retval = [] 4024 vmmap = task.map 4025 pmap = vmmap.pmap 4026 pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops)) 4027 debuglog("pager_ops_addr %s" % hex(pager_ops_addr)) 4028 4029 if unsigned(pmap) == 0: 4030 return retval 4031 vme_list_head = vmmap.hdr.links 4032 vme_ptr_type = gettype('vm_map_entry *') 4033 for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'): 4034 #print vme 4035 if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0: 4036 obj = vme.vme_object.vmo_object 4037 else: 4038 continue 4039 4040 while obj != 0: 4041 if obj.pager != 0: 4042 if obj.internal: 4043 pass 4044 else: 4045 vn_pager = Cast(obj.pager, 'vnode_pager *') 4046 if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn): 4047 retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection))) 4048 obj = obj.shadow 4049 return retval 4050 4051@lldb_command('showtaskloadinfo') 4052def ShowTaskLoadInfo(cmd_args=None, cmd_options={}): 4053 """ Print the load address and uuid for the process 4054 Usage: (lldb)showtaskloadinfo <task_t> 4055 """ 4056 if not cmd_args: 4057 raise ArgumentError("Insufficient arguments") 4058 t = kern.GetValueFromAddress(cmd_args[0], 'struct task *') 4059 print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}" 4060 p = Cast(t.bsd_info, 'struct proc *') 4061 uuid = ProcGetUUID(p) 4062 uuid_out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=uuid) 4063 filepath = GetVnodePath(p.p_textvp) 4064 libname = filepath.split('/')[-1] 4065 #print "uuid: %s file: %s" % (uuid_out_string, filepath) 4066 mappings = FindVMEntriesForVnode(t, p.p_textvp) 4067 load_addr = 0 4068 end_addr = 0 4069 for m in mappings: 4070 if m[3] == 5: 4071 load_addr = m[1] 4072 end_addr = m[2] 4073 #print "Load address: %s" % hex(m[1]) 4074 print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath) 4075 return None 4076 4077@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object")) 4078@lldb_command('vmpagelookup') 4079def VMPageLookup(cmd_args=None): 4080 """ Print the pages in the page bucket corresponding to the provided object and offset. 4081 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t> 4082 """ 4083 if cmd_args == None or len(cmd_args) < 2: 4084 raise ArgumentError("Please specify an object and offset.") 4085 format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n" 4086 4087 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long') 4088 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long') 4089 4090 hash_id = _calc_vm_page_hash(obj, off) 4091 4092 page_list = kern.globals.vm_page_buckets[hash_id].page_list 4093 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list))) 4094 4095 print VMPageLookup.header 4096 page = _vm_page_unpack_ptr(page_list) 4097 while (page != 0) : 4098 pg_t = kern.GetValueFromAddress(page, 'vm_page_t') 4099 print format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object)) 4100 page = _vm_page_unpack_ptr(pg_t.vmp_next_m) 4101 4102 4103 4104@lldb_command('vmpage_get_phys_page') 4105def VmPageGetPhysPage(cmd_args=None): 4106 """ return the physical page for a vm_page_t 4107 usage: vm_page_get_phys_page <vm_page_t> 4108 """ 4109 if cmd_args == None or len(cmd_args) < 1: 4110 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help." 4111 return 4112 4113 page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t') 4114 phys_page = _vm_page_get_phys_page(page) 4115 print("phys_page = 0x%x\n" % phys_page) 4116 4117 4118def _vm_page_get_phys_page(page): 4119 if kern.arch == 'x86_64': 4120 return page.vmp_phys_page 4121 4122 if page == 0 : 4123 return 0 4124 4125 m = unsigned(page) 4126 if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) : 4127 return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum) 4128 4129 page_with_ppnum = Cast(page, 'uint32_t *') 4130 ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t') 4131 return page_with_ppnum[ppnum_offset] 4132 4133 4134@lldb_command('vmpage_unpack_ptr') 4135def VmPageUnpackPtr(cmd_args=None): 4136 """ unpack a pointer 4137 usage: vm_page_unpack_ptr <packed_ptr> 4138 """ 4139 if cmd_args == None or len(cmd_args) < 1: 4140 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help." 4141 return 4142 4143 packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long') 4144 unpacked = _vm_page_unpack_ptr(packed) 4145 print("unpacked pointer = 0x%x\n" % unpacked) 4146 4147 4148def _vm_page_unpack_ptr(page): 4149 if kern.ptrsize == 4 : 4150 return page 4151 4152 if page == 0 : 4153 return page 4154 4155 params = kern.globals.vm_page_packing_params 4156 ptr_shift = params.vmpp_shift 4157 ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask 4158 4159 # when no mask and shift on 64bit systems, we're working with real/non-packed pointers 4160 if ptr_shift == 0 and ptr_mask == 0: 4161 return page 4162 4163 if unsigned(page) & unsigned(ptr_mask): 4164 masked_page = (unsigned(page) & ~ptr_mask) 4165 # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge 4166 vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0])) 4167 element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr 4168 return (vm_pages_addr + masked_page * element_size) 4169 return unsigned(vm_unpack_pointer(page, params)) 4170 4171@lldb_command('calcvmpagehash') 4172def CalcVMPageHash(cmd_args=None): 4173 """ Get the page bucket corresponding to the provided object and offset. 4174 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t> 4175 """ 4176 if cmd_args == None or len(cmd_args) < 2: 4177 raise ArgumentError("Please specify an object and offset.") 4178 4179 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long') 4180 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long') 4181 4182 hash_id = _calc_vm_page_hash(obj, off) 4183 4184 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list))) 4185 return None 4186 4187def _calc_vm_page_hash(obj, off): 4188 bucket_hash = (int) (kern.globals.vm_page_bucket_hash) 4189 hash_mask = (int) (kern.globals.vm_page_hash_mask) 4190 4191 one = (obj * bucket_hash) & 0xFFFFFFFF 4192 two = off >> unsigned(kern.globals.page_shift) 4193 three = two ^ bucket_hash 4194 four = one + three 4195 hash_id = four & hash_mask 4196 4197 return hash_id 4198 4199#Macro: showallocatedzoneelement 4200@lldb_command('showallocatedzoneelement') 4201def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}): 4202 """ Show all the allocated elements in a zone 4203 usage: showzoneallocelements <address of zone> 4204 """ 4205 if len(cmd_args) < 1: 4206 raise ArgumentError("Please specify a zone") 4207 4208 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *') 4209 elements = FindAllocatedElementsInZone(zone) 4210 i = 1 4211 for elem in elements: 4212 print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i, len(elements), elem) 4213 i += 1 4214 4215#EndMacro: showallocatedzoneelement 4216 4217def FindAllocatedElementsInZone(zone): 4218 elements = [] 4219 4220 if not zone.z_self or zone.z_permanent: 4221 return elements 4222 4223 for head in [zone.z_pageq_partial, zone.z_pageq_full]: 4224 for meta in ZoneIteratePageQueue(head): 4225 for elem in meta.iterateElements(): 4226 if not meta.isElementFree(elem): 4227 elements.append(elem) 4228 4229 return elements 4230 4231def match_vm_page_attributes(page, matching_attributes): 4232 page_ptr = addressof(page) 4233 unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object) 4234 matched_attributes = 0 4235 if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]): 4236 matched_attributes += 1 4237 if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])): 4238 matched_attributes += 1 4239 if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])): 4240 matched_attributes += 1 4241 if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])): 4242 matched_attributes += 1 4243 if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1: 4244 matched_attributes += 1 4245 4246 return matched_attributes 4247 4248#Macro scan_vm_pages 4249@header("{0: >26s}{1: >20s}{2: >10s}{3: >20s}{4: >20s}{5: >16s}".format("vm_pages_index/zone", "vm_page", "q_state", "vm_object", "offset", "ppn", "bitfield", "from_zone_map")) 4250@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA') 4251def ScanVMPages(cmd_args=None, cmd_options={}): 4252 """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes. 4253 usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone] 4254 4255 scan_vm_pages -A: scan vm pages in the global vm_pages array 4256 scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone 4257 scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue 4258 scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object 4259 scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value 4260 scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number 4261 scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set 4262 scan_vm_pages <-A> -I <start_index>: Start the scan from start_index 4263 scan_vm_pages <-A> -N <npages>: Scan at most npages 4264 """ 4265 if (len(cmd_options) < 1): 4266 raise ArgumentError("Please specify at least one matching attribute") 4267 4268 vm_pages = kern.globals.vm_pages 4269 vm_pages_count = kern.globals.vm_pages_count 4270 4271 start_index = 0 4272 npages = vm_pages_count 4273 scan_vmpages_array = False 4274 scan_vmpages_zone = False 4275 attribute_count = 0 4276 4277 if "-A" in cmd_options: 4278 scan_vmpages_array = True 4279 4280 if "-Z" in cmd_options: 4281 scan_vmpages_zone = True 4282 4283 if scan_vmpages_array == False and scan_vmpages_zone == False: 4284 raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)") 4285 4286 attribute_values = {} 4287 if "-S" in cmd_options: 4288 attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int') 4289 attribute_count += 1 4290 4291 if "-O" in cmd_options: 4292 attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t') 4293 attribute_count += 1 4294 4295 if "-F" in cmd_options: 4296 attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long') 4297 attribute_count += 1 4298 4299 if "-P" in cmd_options: 4300 attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int') 4301 attribute_count += 1 4302 4303 if "-B" in cmd_options: 4304 valid_vmp_bitfields = [ 4305 "vmp_in_background", 4306 "vmp_on_backgroundq", 4307 "vmp_gobbled", 4308 "vmp_laundry", 4309 "vmp_no_cache", 4310 "vmp_private", 4311 "vmp_reference", 4312 "vmp_busy", 4313 "vmp_wanted", 4314 "vmp_tabled", 4315 "vmp_hashed", 4316 "vmp_fictitious", 4317 "vmp_clustered", 4318 "vmp_pmapped", 4319 "vmp_xpmapped", 4320 "vmp_free_when_done", 4321 "vmp_absent", 4322 "vmp_error", 4323 "vmp_dirty", 4324 "vmp_cleaning", 4325 "vmp_precious", 4326 "vmp_overwriting", 4327 "vmp_restart", 4328 "vmp_unusual", 4329 "vmp_cs_validated", 4330 "vmp_cs_tainted", 4331 "vmp_cs_nx", 4332 "vmp_reusable", 4333 "vmp_lopage", 4334 "vmp_written_by_kernel", 4335 "vmp_unused_object_bits" 4336 ] 4337 attribute_values["bitfield"] = cmd_options["-B"] 4338 if attribute_values["bitfield"] in valid_vmp_bitfields: 4339 attribute_count += 1 4340 else: 4341 raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield)) 4342 4343 if "-I" in cmd_options: 4344 start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int') 4345 npages = vm_pages_count - start_index 4346 4347 if "-N" in cmd_options: 4348 npages = kern.GetValueFromAddress(cmd_options["-N"], 'int') 4349 if npages == 0: 4350 raise ArgumentError("You specified -N 0, nothing to be scanned") 4351 4352 end_index = start_index + npages - 1 4353 if end_index >= vm_pages_count: 4354 raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count)) 4355 4356 header_after_n_lines = 40 4357 format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}" 4358 4359 found_in_array = 0 4360 if scan_vmpages_array: 4361 print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count) 4362 i = start_index 4363 while i <= end_index: 4364 page = vm_pages[i] 4365 if match_vm_page_attributes(page, attribute_values) == attribute_count: 4366 if found_in_array % header_after_n_lines == 0: 4367 print ScanVMPages.header 4368 4369 print format_string.format(str(i), addressof(page), page.vmp_q_state, _vm_page_unpack_ptr(page.vmp_object), page.vmp_offset, _vm_page_get_phys_page(addressof(page))) 4370 found_in_array += 1 4371 4372 i += 1 4373 4374 found_in_zone = 0 4375 if scan_vmpages_zone: 4376 page_size = kern.GetGlobalVariable('page_size') 4377 print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count) 4378 4379 zone = GetZoneByName("vm pages") 4380 if zone is None: 4381 print "Cannot find vm_pages zone, skip the scan" 4382 else: 4383 print "Scanning page queues in the vm_pages zone..." 4384 elements = FindAllocatedElementsInZone(zone) 4385 for elem in elements: 4386 page = kern.GetValueFromAddress(elem, 'vm_page_t') 4387 4388 if match_vm_page_attributes(page, attribute_values) == attribute_count: 4389 if found_in_zone % header_after_n_lines == 0: 4390 print ScanVMPages.header 4391 4392 vm_object = _vm_page_unpack_ptr(page.vmp_object) 4393 phys_page = _vm_page_get_phys_page(page) 4394 print format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page) 4395 found_in_zone += 1 4396 4397 total = found_in_array + found_in_zone 4398 print "Found {0:d} vm pages ({1:d} in array, {2:d} in zone) matching the requested {3:d} attribute(s)".format(total, found_in_array, found_in_zone, attribute_count) 4399 4400#EndMacro scan_vm_pages 4401 4402VM_PAGE_IS_WIRED = 1 4403 4404@header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield")) 4405@lldb_command('vmobjectwalkpages', 'CSBNQP:O:') 4406def VMObjectWalkPages(cmd_args=None, cmd_options={}): 4407 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we 4408 specifically look for this page, highlighting it in the output or noting if it was not found. For 4409 each page, we confirm that it points to the object. We also keep track of the number of pages we 4410 see and compare this to the object's resident page count field. 4411 Usage: 4412 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default) 4413 vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages 4414 vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards 4415 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit 4416 vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. q_state == VM_PAGE_IS_WIRED && wire_count == 0) 4417 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with *** 4418 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page 4419 vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset 4420 4421 """ 4422 4423 if (cmd_args == None or len(cmd_args) < 1): 4424 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t") 4425 4426 out_string = "" 4427 4428 obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 4429 4430 page = 0 4431 if "-P" in cmd_options: 4432 page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t') 4433 4434 off = -1 4435 if "-O" in cmd_options: 4436 off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t') 4437 4438 stop = 0 4439 if "-S" in cmd_options: 4440 if page == 0 and off < 0: 4441 raise ArgumentError("-S can only be passed when a page is specified with -P or -O") 4442 stop = 1 4443 4444 walk_backwards = False 4445 if "-B" in cmd_options: 4446 walk_backwards = True 4447 4448 quiet_mode = False 4449 if "-Q" in cmd_options: 4450 quiet_mode = True 4451 4452 if not quiet_mode: 4453 print VMObjectWalkPages.header 4454 format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t" 4455 first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t" 4456 second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:" 4457 second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:" 4458 second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:" 4459 second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n" 4460 4461 limit = 4096 #arbitrary limit of number of pages to walk 4462 ignore_limit = 0 4463 if "-N" in cmd_options: 4464 ignore_limit = 1 4465 4466 show_compressed = 0 4467 if "-C" in cmd_options: 4468 show_compressed = 1 4469 4470 page_count = 0 4471 res_page_count = unsigned(obj.resident_page_count) 4472 page_found = False 4473 pages_seen = set() 4474 4475 for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr): 4476 page_count += 1 4477 out_string = "" 4478 if (page != 0 and not(page_found) and vmp == page): 4479 out_string += "******" 4480 page_found = True 4481 4482 if (off > 0 and not(page_found) and vmp.vmp_offset == off): 4483 out_string += "******" 4484 page_found = True 4485 4486 if page != 0 or off > 0 or quiet_mode: 4487 if (page_count % 1000) == 0: 4488 print "traversed %d pages ...\n" % (page_count) 4489 else: 4490 out_string += format_string.format(page_count, res_page_count, vmp, vmp.vmp_offset, _vm_page_unpack_ptr(vmp.vmp_listq.next), _vm_page_get_phys_page(vmp), vmp.vmp_wire_count) 4491 out_string += first_bitfield_format_string.format(vmp.vmp_q_state, vmp.vmp_in_background, vmp.vmp_on_backgroundq, vmp.vmp_gobbled, vmp.vmp_laundry, vmp.vmp_no_cache, 4492 vmp.vmp_private, vmp.vmp_reference) 4493 4494 if hasattr(vmp,'slid'): 4495 vmp_slid = vmp.slid 4496 else: 4497 vmp_slid = 0 4498 out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp.vmp_fictitious, vmp.vmp_clustered, 4499 vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent, 4500 vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting, 4501 vmp.vmp_restart, vmp.vmp_unusual, 0, 0, 4502 vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid, 4503 vmp.vmp_written_by_kernel) 4504 4505 if (vmp in pages_seen): 4506 print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n" 4507 return 4508 4509 if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)): 4510 print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp.vmp_object))) 4511 return 4512 4513 if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0): 4514 print out_string + " page in wired state with wire_count of 0\n" 4515 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n" 4516 print "stopping...\n" 4517 return 4518 4519 if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)): 4520 print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d\n" % (vmp.vmp_unused_page_bits) 4521 print "stopping...\n" 4522 return 4523 4524 if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)): 4525 print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused_object_bits : %d\n" % (vmp.vmp_unused_object_bits) 4526 print "stopping...\n" 4527 return 4528 4529 pages_seen.add(vmp) 4530 4531 if False: 4532 hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset) 4533 hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list 4534 hash_page = _vm_page_unpack_ptr(hash_page_list) 4535 hash_page_t = 0 4536 4537 while (hash_page != 0): 4538 hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t') 4539 if hash_page_t == vmp: 4540 break 4541 hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m) 4542 4543 if (unsigned(vmp) != unsigned(hash_page_t)): 4544 print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n" 4545 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset))) 4546 return 4547 4548 if (page_count >= limit and not(ignore_limit)): 4549 print out_string + "Limit reached (%d pages), stopping..." % (limit) 4550 break 4551 4552 print out_string 4553 4554 if page_found and stop: 4555 print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count))) 4556 return 4557 4558 if (page != 0): 4559 print("page found? : %s\n" % page_found) 4560 4561 if (off > 0): 4562 print("page found? : %s\n" % page_found) 4563 4564 print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count))) 4565 4566 if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)): 4567 pager = Cast(obj.pager, 'compressor_pager *') 4568 chunks = pager.cpgr_num_slots / 128 4569 pagesize = kern.globals.page_size 4570 4571 page_idx = 0 4572 while page_idx < pager.cpgr_num_slots: 4573 if chunks != 0: 4574 chunk = pager.cpgr_slots.cpgr_islots[page_idx / 128] 4575 slot = chunk[page_idx % 128] 4576 elif pager.cpgr_num_slots > 2: 4577 slot = pager.cpgr_slots.cpgr_dslots[page_idx] 4578 else: 4579 slot = pager.cpgr_slots.cpgr_eslots[page_idx] 4580 4581 if slot != 0: 4582 print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot)) 4583 page_idx = page_idx + 1 4584 4585 4586@lldb_command("show_all_apple_protect_pagers") 4587def ShowAllAppleProtectPagers(cmd_args=None): 4588 """Routine to print all apple_protect pagers 4589 usage: show_all_apple_protect_pagers 4590 """ 4591 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "cached", "object", "offset", "crypto_offset", "crypto_start", "crypto_end") 4592 qhead = kern.globals.apple_protect_pager_queue 4593 qtype = GetType('apple_protect_pager *') 4594 qcnt = kern.globals.apple_protect_pager_count 4595 idx = 0 4596 for pager in IterateQueue(qhead, qtype, "pager_queue"): 4597 idx = idx + 1 4598 show_apple_protect_pager(pager, qcnt, idx) 4599 4600@lldb_command("show_apple_protect_pager") 4601def ShowAppleProtectPager(cmd_args=None): 4602 """Routine to print out info about an apple_protect pager 4603 usage: show_apple_protect_pager <pager> 4604 """ 4605 if cmd_args == None or len(cmd_args) < 1: 4606 print "Invalid argument.", ShowAppleProtectPager.__doc__ 4607 return 4608 pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t') 4609 show_apple_protect_pager(pager, 1, 1) 4610 4611def show_apple_protect_pager(pager, qcnt, idx): 4612 object = pager.backing_object 4613 shadow = object.shadow 4614 while shadow != 0: 4615 object = shadow 4616 shadow = object.shadow 4617 vnode_pager = Cast(object.pager,'vnode_pager *') 4618 filename = GetVnodePath(vnode_pager.vnode_handle) 4619 if hasattr(pager, "ap_pgr_hdr_ref"): 4620 refcnt = pager.ap_pgr_hdr_ref 4621 else: 4622 refcnt = pager.ap_pgr_hdr.mo_ref 4623 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{: <#018x} <decrypt:{: <#018x} end:{:#018x} ops:{: <#018x} refs:{:<d}>\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, refcnt, pager.is_ready, pager.is_mapped, pager.is_cached, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename) 4624 showvmobject(pager.backing_object, pager.backing_offset, pager.crypto_end - pager.crypto_start, 1, 1) 4625 4626@lldb_command("show_all_shared_region_pagers") 4627def ShowAllSharedRegionPagers(cmd_args=None): 4628 """Routine to print all shared_region pagers 4629 usage: show_all_shared_region_pagers 4630 """ 4631 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "offset", "jop_key", "slide", "slide_info") 4632 qhead = kern.globals.shared_region_pager_queue 4633 qtype = GetType('shared_region_pager *') 4634 qcnt = kern.globals.shared_region_pager_count 4635 idx = 0 4636 for pager in IterateQueue(qhead, qtype, "srp_queue"): 4637 idx = idx + 1 4638 show_shared_region_pager(pager, qcnt, idx) 4639 4640@lldb_command("show_shared_region_pager") 4641def ShowSharedRegionPager(cmd_args=None): 4642 """Routine to print out info about a shared_region pager 4643 usage: show_shared_region_pager <pager> 4644 """ 4645 if cmd_args == None or len(cmd_args) < 1: 4646 print "Invalid argument.", ShowSharedRegionPager.__doc__ 4647 return 4648 pager = kern.GetValueFromAddress(cmd_args[0], 'shared_region_pager_t') 4649 show_shared_region_pager(pager, 1, 1) 4650 4651def show_shared_region_pager(pager, qcnt, idx): 4652 object = pager.srp_backing_object 4653 shadow = object.shadow 4654 while shadow != 0: 4655 object = shadow 4656 shadow = object.shadow 4657 vnode_pager = Cast(object.pager,'vnode_pager *') 4658 filename = GetVnodePath(vnode_pager.vnode_handle) 4659 if hasattr(pager, 'srp_ref_count'): 4660 ref_count = pager.srp_ref_count 4661 else: 4662 ref_count = pager.srp_header.mo_ref 4663 if hasattr(pager, 'srp_jop_key'): 4664 jop_key = pager.srp_jop_key 4665 else: 4666 jop_key = -1 4667 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x}\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, ref_count, pager.srp_is_ready, pager.srp_is_mapped, pager.srp_backing_object, pager.srp_backing_offset, jop_key, pager.srp_slide_info.si_slide, pager.srp_slide_info, vnode_pager.vnode_handle, filename) 4668 showvmobject(pager.srp_backing_object, pager.srp_backing_offset, pager.srp_slide_info.si_end - pager.srp_slide_info.si_start, 1, 1) 4669 4670@lldb_command("show_console_ring") 4671def ShowConsoleRingData(cmd_args=None): 4672 """ Print console ring buffer stats and data 4673 """ 4674 cr = kern.globals.console_ring 4675 print "console_ring = {:#018x} buffer = {:#018x} length = {:<5d} used = {:<5d} read_ptr = {:#018x} write_ptr = {:#018x}".format(addressof(cr), cr.buffer, cr.len, cr.used, cr.read_ptr, cr.write_ptr) 4676 pending_data = [] 4677 for i in range(unsigned(cr.used)): 4678 idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len) 4679 pending_data.append("{:c}".format(cr.buffer[idx])) 4680 4681 if pending_data: 4682 print "Data:" 4683 print "".join(pending_data) 4684 4685# Macro: showjetsamsnapshot 4686 4687@lldb_command("showjetsamsnapshot", "DA") 4688def ShowJetsamSnapshot(cmd_args=None, cmd_options={}): 4689 """ Dump entries in the jetsam snapshot table 4690 usage: showjetsamsnapshot [-D] [-A] 4691 Use -D flag to print extra physfootprint details 4692 Use -A flag to print all entries (regardless of valid count) 4693 """ 4694 4695 # Not shown are uuid, user_data, cpu_time 4696 4697 global kern 4698 4699 show_footprint_details = False 4700 show_all_entries = False 4701 4702 if "-D" in cmd_options: 4703 show_footprint_details = True 4704 4705 if "-A" in cmd_options: 4706 show_all_entries = True 4707 4708 valid_count = kern.globals.memorystatus_jetsam_snapshot_count 4709 max_count = kern.globals.memorystatus_jetsam_snapshot_max 4710 4711 if (show_all_entries == True): 4712 count = max_count 4713 else: 4714 count = valid_count 4715 4716 print "{:s}".format(valid_count) 4717 print "{:s}".format(max_count) 4718 4719 if int(count) == 0: 4720 print "The jetsam snapshot is empty." 4721 print "Use -A to force dump all entries (regardless of valid count)" 4722 return 4723 4724 # Dumps the snapshot header info 4725 print lldb_run_command('p *memorystatus_jetsam_snapshot') 4726 4727 hdr_format = "{0: >32s} {1: >5s} {2: >4s} {3: >6s} {4: >6s} {5: >20s} {6: >20s} {7: >20s} {8: >5s} {9: >10s} {10: >6s} {11: >6s} {12: >10s} {13: >15s} {14: >15s} {15: >15s}" 4728 if (show_footprint_details == True): 4729 hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}" 4730 4731 4732 if (show_footprint_details == False): 4733 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax') 4734 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)') 4735 else: 4736 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table') 4737 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)') 4738 4739 4740 entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\ 4741 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\ 4742 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\ 4743 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\ 4744 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}" 4745 4746 if (show_footprint_details == True): 4747 entry_format += "{e.jse_internal_pages: >15d} "\ 4748 "{e.jse_internal_compressed_pages: >15d} "\ 4749 "{e.jse_iokit_mapped_pages: >12d} "\ 4750 "{e.jse_purgeable_nonvolatile_pages: >12d} "\ 4751 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\ 4752 "{e.jse_alternate_accounting_pages: >10d} "\ 4753 "{e.jse_alternate_accounting_compressed_pages: >13d} "\ 4754 "{e.jse_page_table_pages: >10d}" 4755 4756 snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries 4757 idx = 0 4758 while idx < count: 4759 current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *')) 4760 print entry_format.format(index=idx, e=current_entry) 4761 idx +=1 4762 return 4763 4764# EndMacro: showjetsamsnapshot 4765 4766# Macro: showvnodecleanblk/showvnodedirtyblk 4767 4768def _GetBufSummary(buf): 4769 """ Get a summary of important information out of a buf_t. 4770 """ 4771 initial = "(struct buf) {0: <#0x} =" 4772 4773 # List all of the fields in this buf summary. 4774 entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq, 4775 buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid, 4776 buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp, 4777 buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata, 4778 buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff, 4779 buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr] 4780 4781 # Join an (already decent) string representation of each field 4782 # with newlines and indent the region. 4783 joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ") 4784 4785 # Add the total string representation to our title and return it. 4786 out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n" 4787 return out_str 4788 4789def _ShowVnodeBlocks(dirty=True, cmd_args=None): 4790 """ Display info about all [dirty|clean] blocks in a vnode. 4791 """ 4792 if cmd_args == None or len(cmd_args) < 1: 4793 print "Please provide a valid vnode argument." 4794 return 4795 4796 vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 4797 list_head = vnodeval.v_cleanblkhd; 4798 if dirty: 4799 list_head = vnodeval.v_dirtyblkhd 4800 4801 print "Blocklist for vnode {}:".format(cmd_args[0]) 4802 4803 i = 0 4804 for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'): 4805 # For each block (buf_t) in the appropriate list, 4806 # ask for a summary and print it. 4807 print "---->\nblock {}: ".format(i) + _GetBufSummary(buf) 4808 i += 1 4809 return 4810 4811@lldb_command('showvnodecleanblk') 4812def ShowVnodeCleanBlocks(cmd_args=None): 4813 """ Display info about all clean blocks in a vnode. 4814 usage: showvnodecleanblk <address of vnode> 4815 """ 4816 _ShowVnodeBlocks(False, cmd_args) 4817 4818@lldb_command('showvnodedirtyblk') 4819def ShowVnodeDirtyBlocks(cmd_args=None): 4820 """ Display info about all dirty blocks in a vnode. 4821 usage: showvnodedirtyblk <address of vnode> 4822 """ 4823 _ShowVnodeBlocks(True, cmd_args) 4824 4825# EndMacro: showvnodecleanblk/showvnodedirtyblk 4826 4827 4828@lldb_command("vm_page_lookup_in_map") 4829def VmPageLookupInMap(cmd_args=None): 4830 """Lookup up a page at a virtual address in a VM map 4831 usage: vm_page_lookup_in_map <map> <vaddr> 4832 """ 4833 if cmd_args == None or len(cmd_args) < 2: 4834 print "Invalid argument.", VmPageLookupInMap.__doc__ 4835 return 4836 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 4837 vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t') 4838 print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map) 4839 vm_page_lookup_in_map(map, vaddr) 4840 4841def vm_page_lookup_in_map(map, vaddr): 4842 vaddr = unsigned(vaddr) 4843 vme_list_head = map.hdr.links 4844 vme_ptr_type = GetType('vm_map_entry *') 4845 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 4846 if unsigned(vme.links.start) > vaddr: 4847 break 4848 if unsigned(vme.links.end) <= vaddr: 4849 continue 4850 offset_in_vme = vaddr - unsigned(vme.links.start) 4851 print " offset {:#018x} in map entry {: <#018x} [{:#018x}:{:#018x}] object {: <#018x} offset {:#018x}".format(offset_in_vme, vme, unsigned(vme.links.start), unsigned(vme.links.end), vme.vme_object.vmo_object, unsigned(vme.vme_offset) & ~0xFFF) 4852 offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF) 4853 if vme.is_sub_map: 4854 print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap) 4855 vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object) 4856 else: 4857 vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object) 4858 4859@lldb_command("vm_page_lookup_in_object") 4860def VmPageLookupInObject(cmd_args=None): 4861 """Lookup up a page at a given offset in a VM object 4862 usage: vm_page_lookup_in_object <object> <offset> 4863 """ 4864 if cmd_args == None or len(cmd_args) < 2: 4865 print "Invalid argument.", VmPageLookupInObject.__doc__ 4866 return 4867 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 4868 offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t') 4869 print "offset {:#018x} in object {: <#018x}".format(offset, object) 4870 vm_page_lookup_in_object(object, offset) 4871 4872def vm_page_lookup_in_object(object, offset): 4873 offset = unsigned(offset) 4874 page_size = kern.globals.page_size 4875 trunc_offset = offset & ~(page_size - 1) 4876 print " offset {:#018x} in VM object {: <#018x}".format(offset, object) 4877 hash_id = _calc_vm_page_hash(object, trunc_offset) 4878 page_list = kern.globals.vm_page_buckets[hash_id].page_list 4879 page = _vm_page_unpack_ptr(page_list) 4880 while page != 0: 4881 m = kern.GetValueFromAddress(page, 'vm_page_t') 4882 m_object_val = _vm_page_unpack_ptr(m.vmp_object) 4883 m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t') 4884 if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset): 4885 page = _vm_page_unpack_ptr(m.vmp_next_m) 4886 continue 4887 print " resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m)) 4888 return 4889 if object.pager and object.pager_ready: 4890 offset_in_pager = trunc_offset + unsigned(object.paging_offset) 4891 if not object.internal: 4892 print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager) 4893 return 4894 pager = Cast(object.pager, 'compressor_pager *') 4895 ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager) 4896 if ret: 4897 return 4898 if object.shadow and not object.phys_contiguous: 4899 offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset) 4900 vm_page_lookup_in_object(object.shadow, offset_in_shadow) 4901 return 4902 print " page is absent and will be zero-filled on demand" 4903 return 4904 4905@lldb_command("vm_page_lookup_in_compressor_pager") 4906def VmPageLookupInCompressorPager(cmd_args=None): 4907 """Lookup up a page at a given offset in a compressor pager 4908 usage: vm_page_lookup_in_compressor_pager <pager> <offset> 4909 """ 4910 if cmd_args == None or len(cmd_args) < 2: 4911 print "Invalid argument.", VmPageLookupInCompressorPager.__doc__ 4912 return 4913 pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t') 4914 offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t') 4915 print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager) 4916 vm_page_lookup_in_compressor_pager(pager, offset) 4917 4918def vm_page_lookup_in_compressor_pager(pager, offset): 4919 offset = unsigned(offset) 4920 page_size = unsigned(kern.globals.page_size) 4921 page_num = unsigned(offset / page_size) 4922 if page_num > pager.cpgr_num_slots: 4923 print " *** ERROR: vm_page_lookup_in_compressor_pager({: <#018x},{:#018x}): page_num {:#x} > num_slots {:#x}".format(pager, offset, page_num, pager.cpgr_num_slots) 4924 return 0 4925 slots_per_chunk = 512 / sizeof ('compressor_slot_t') 4926 num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk) 4927 if num_chunks > 1: 4928 chunk_idx = unsigned(page_num / slots_per_chunk) 4929 chunk = pager.cpgr_slots.cpgr_islots[chunk_idx] 4930 slot_idx = unsigned(page_num % slots_per_chunk) 4931 slot = GetObjectAtIndexFromArray(chunk, slot_idx) 4932 slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx) 4933 elif pager.cpgr_num_slots > 2: 4934 slot_idx = page_num 4935 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx) 4936 slot_str = "dslots[{:d}]".format(slot_idx) 4937 else: 4938 slot_idx = page_num 4939 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx) 4940 slot_str = "eslots[{:d}]".format(slot_idx) 4941 print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot) 4942 if slot == 0: 4943 return 0 4944 slot_value = dereference(slot) 4945 print " value {:#010x}".format(slot_value) 4946 vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t')) 4947 return 1 4948 4949@lldb_command("vm_page_lookup_in_compressor") 4950def VmPageLookupInCompressor(cmd_args=None): 4951 """Lookup up a page in a given compressor slot 4952 usage: vm_page_lookup_in_compressor <slot> 4953 """ 4954 if cmd_args == None or len(cmd_args) < 1: 4955 print "Invalid argument.", VmPageLookupInCompressor.__doc__ 4956 return 4957 slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *') 4958 print "compressor slot {: <#018x}".format(slot) 4959 vm_page_lookup_in_compressor(slot) 4960 4961C_SV_CSEG_ID = ((1 << 22) - 1) 4962 4963def vm_page_lookup_in_compressor(slot_ptr): 4964 slot_ptr = Cast(slot_ptr, 'compressor_slot_t *') 4965 slot_value = dereference(slot_ptr) 4966 slot = Cast(slot_value, 'c_slot_mapping') 4967 print slot 4968 print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx) 4969 if slot_ptr == 0: 4970 return 4971 if slot.s_cseg == C_SV_CSEG_ID: 4972 sv = kern.globals.c_segment_sv_hash_table 4973 print "single value[{:#d}]: ref {:d} value {:#010x}".format(slot.s_cindx, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_ref, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_data) 4974 return 4975 if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available): 4976 print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available)) 4977 return 4978 c_segments = kern.globals.c_segments 4979 c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1) 4980 c_seg = c_segments_elt.c_seg 4981 c_no_data = 0 4982 if hasattr(c_seg, 'c_state'): 4983 c_state = c_seg.c_state 4984 if c_state == 0: 4985 c_state_str = "C_IS_EMPTY" 4986 c_no_data = 1 4987 elif c_state == 1: 4988 c_state_str = "C_IS_FREE" 4989 c_no_data = 1 4990 elif c_state == 2: 4991 c_state_str = "C_IS_FILLING" 4992 elif c_state == 3: 4993 c_state_str = "C_ON_AGE_Q" 4994 elif c_state == 4: 4995 c_state_str = "C_ON_SWAPOUT_Q" 4996 elif c_state == 5: 4997 c_state_str = "C_ON_SWAPPEDOUT_Q" 4998 c_no_data = 1 4999 elif c_state == 6: 5000 c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q" 5001 c_no_data = 1 5002 elif c_state == 7: 5003 c_state_str = "C_ON_SWAPPEDIN_Q" 5004 elif c_state == 8: 5005 c_state_str = "C_ON_MAJORCOMPACT_Q" 5006 elif c_state == 9: 5007 c_state_str = "C_ON_BAD_Q" 5008 c_no_data = 1 5009 else: 5010 c_state_str = "<unknown>" 5011 else: 5012 c_state = -1 5013 c_state_str = "<no c_state field>" 5014 print "c_segments[{:d}] {: <#018x} c_seg {: <#018x} c_state {:#x}={:s}".format(slot.s_cseg-1, c_segments_elt, c_seg, c_state, c_state_str) 5015 c_indx = unsigned(slot.s_cindx) 5016 if hasattr(c_seg, 'c_slot_var_array'): 5017 c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len 5018 if c_indx < c_seg_fixed_array_len: 5019 cs = c_seg.c_slot_fixed_array[c_indx] 5020 else: 5021 cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len) 5022 else: 5023 C_SEG_SLOT_ARRAY_SIZE = 64 5024 C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1 5025 cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK) 5026 print cs 5027 c_slot_unpacked_ptr = vm_unpack_ptr(cs.c_packed_ptr, kern.globals.c_slot_packing_params) 5028 print "c_slot {: <#018x} c_offset {:#x} c_size {:#x} c_packed_ptr {:#x} (unpacked: {: <#018x})".format(cs, cs.c_offset, cs.c_size, cs.c_packed_ptr, unsigned(c_slot_unpacked_ptr)) 5029 if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr): 5030 print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr) 5031 if c_no_data == 0: 5032 c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset) 5033 c_size = cs.c_size 5034 cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size) 5035 print cmd 5036 print lldb_run_command(cmd) 5037 else: 5038 print "<no compressed data>" 5039 5040@lldb_command('vm_scan_all_pages') 5041def VMScanAllPages(cmd_args=None): 5042 """Scans the vm_pages[] array 5043 """ 5044 vm_pages_count = kern.globals.vm_pages_count 5045 vm_pages = kern.globals.vm_pages 5046 5047 free_count = 0 5048 local_free_count = 0 5049 active_count = 0 5050 local_active_count = 0 5051 inactive_count = 0 5052 speculative_count = 0 5053 throttled_count = 0 5054 wired_count = 0 5055 compressor_count = 0 5056 pageable_internal_count = 0 5057 pageable_external_count = 0 5058 secluded_count = 0 5059 secluded_free_count = 0 5060 secluded_inuse_count = 0 5061 5062 i = 0 5063 while i < vm_pages_count: 5064 5065 if i % 10000 == 0: 5066 print "{:d}/{:d}...\n".format(i,vm_pages_count) 5067 5068 m = vm_pages[i] 5069 5070 internal = 0 5071 external = 0 5072 m_object_val = _vm_page_unpack_ptr(m.vmp_object) 5073 5074 if m_object: 5075 if m_object.internal: 5076 internal = 1 5077 else: 5078 external = 1 5079 5080 if m.vmp_wire_count != 0 and m.vmp_local == 0: 5081 wired_count = wired_count + 1 5082 pageable = 0 5083 elif m.vmp_throttled: 5084 throttled_count = throttled_count + 1 5085 pageable = 0 5086 elif m.vmp_active: 5087 active_count = active_count + 1 5088 pageable = 1 5089 elif m.vmp_local: 5090 local_active_count = local_active_count + 1 5091 pageable = 0 5092 elif m.vmp_inactive: 5093 inactive_count = inactive_count + 1 5094 pageable = 1 5095 elif m.vmp_speculative: 5096 speculative_count = speculative_count + 1 5097 pageable = 0 5098 elif m.vmp_free: 5099 free_count = free_count + 1 5100 pageable = 0 5101 elif m.vmp_secluded: 5102 secluded_count = secluded_count + 1 5103 if m_object == 0: 5104 secluded_free_count = secluded_free_count + 1 5105 else: 5106 secluded_inuse_count = secluded_inuse_count + 1 5107 pageable = 0 5108 elif m_object == 0 and m.vmp_busy: 5109 local_free_count = local_free_count + 1 5110 pageable = 0 5111 elif m.vmp_compressor: 5112 compressor_count = compressor_count + 1 5113 pageable = 0 5114 else: 5115 print "weird page vm_pages[{:d}]?\n".format(i) 5116 pageable = 0 5117 5118 if pageable: 5119 if internal: 5120 pageable_internal_count = pageable_internal_count + 1 5121 else: 5122 pageable_external_count = pageable_external_count + 1 5123 i = i + 1 5124 5125 print "vm_pages_count = {:d}\n".format(vm_pages_count) 5126 5127 print "wired_count = {:d}\n".format(wired_count) 5128 print "throttled_count = {:d}\n".format(throttled_count) 5129 print "active_count = {:d}\n".format(active_count) 5130 print "local_active_count = {:d}\n".format(local_active_count) 5131 print "inactive_count = {:d}\n".format(inactive_count) 5132 print "speculative_count = {:d}\n".format(speculative_count) 5133 print "free_count = {:d}\n".format(free_count) 5134 print "local_free_count = {:d}\n".format(local_free_count) 5135 print "compressor_count = {:d}\n".format(compressor_count) 5136 5137 print "pageable_internal_count = {:d}\n".format(pageable_internal_count) 5138 print "pageable_external_count = {:d}\n".format(pageable_external_count) 5139 print "secluded_count = {:d}\n".format(secluded_count) 5140 print "secluded_free_count = {:d}\n".format(secluded_free_count) 5141 print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count) 5142 5143 5144@lldb_command('show_all_vm_named_entries') 5145def ShowAllVMNamedEntries(cmd_args=None): 5146 """ Routine to print a summary listing of all the VM named entries 5147 """ 5148 queue_len = kern.globals.vm_named_entry_count 5149 queue_head = kern.globals.vm_named_entry_list 5150 5151 print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len) 5152 5153# print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process") 5154 idx = 0 5155 for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'): 5156 idx += 1 5157 showmemoryentry(entry, idx, queue_len) 5158 5159@lldb_command('show_vm_named_entry') 5160def ShowVMNamedEntry(cmd_args=None): 5161 """ Routine to print a VM named entry 5162 """ 5163 if cmd_args == None or len(cmd_args) < 1: 5164 print "Invalid argument.", ShowMapVMNamedEntry.__doc__ 5165 return 5166 named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t') 5167 showmemoryentry(named_entry, 0, 0) 5168 5169def showmemoryentry(entry, idx=0, queue_len=0): 5170 """ Routine to print out a summary a VM memory entry 5171 params: 5172 entry - core.value : a object of type 'struct vm_named_entry *' 5173 returns: 5174 None 5175 """ 5176 show_pager_info = True 5177 show_all_shadows = True 5178 5179 backing = "" 5180 if entry.is_sub_map == 1: 5181 backing += "SUBMAP" 5182 if entry.is_copy == 1: 5183 backing += "COPY" 5184 if entry.is_object == 1: 5185 backing += "OBJECT" 5186 if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0: 5187 backing += "***?***" 5188 prot="" 5189 if entry.protection & 0x1: 5190 prot += "r" 5191 else: 5192 prot += "-" 5193 if entry.protection & 0x2: 5194 prot += "w" 5195 else: 5196 prot += "-" 5197 if entry.protection & 0x4: 5198 prot += "x" 5199 else: 5200 prot += "-" 5201 extra_str = "" 5202 if hasattr(entry, 'named_entry_alias'): 5203 extra_str += " alias={:d}".format(entry.named_entry_alias) 5204 if hasattr(entry, 'named_entry_port'): 5205 extra_str += " port={:#016x}".format(entry.named_entry_port) 5206 print "{:d}/{:d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx,queue_len,entry,entry.ref_count,entry.protection,prot,backing,entry.backing.copy,entry.offset,entry.data_offset,entry.size,extra_str) 5207 if entry.is_sub_map == 1: 5208 showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows) 5209 elif entry.is_copy == 1: 5210 showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0) 5211 elif entry.is_object == 1: 5212 showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0) 5213 else: 5214 print "***** UNKNOWN TYPE *****" 5215 print " \n" 5216 5217 5218def IterateRBTreeEntry2(element, element_type, field_name1, field_name2): 5219 """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h 5220 element - value : Value object for rbh_root 5221 element_type - str : Type of the link element 5222 field_name - str : Name of the field in link element's structure 5223 returns: 5224 A generator does not return. It is used for iterating 5225 value : an object thats of type (element_type) head->sle_next. Always a pointer object 5226 """ 5227 elt = element.__getattr__('rbh_root') 5228 if type(element_type) == str: 5229 element_type = gettype(element_type) 5230 charp_type = gettype('char *'); 5231 5232 # Walk to find min 5233 parent = elt 5234 while unsigned(elt) != 0: 5235 parent = elt 5236 elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type) 5237 elt = parent 5238 5239 # Now elt is min 5240 while unsigned(elt) != 0: 5241 yield elt 5242 # implementation cribbed from RB_NEXT in libkern/tree.h 5243 right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type) 5244 if unsigned(right) != 0: 5245 elt = right 5246 left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type) 5247 while unsigned(left) != 0: 5248 elt = left 5249 left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type) 5250 else: 5251 5252 # avoid using GetValueFromAddress 5253 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1 5254 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr))) 5255 parent = cast(parent, element_type) 5256 5257 if unsigned(parent) != 0: 5258 left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type) 5259 if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)): 5260 elt = parent 5261 else: 5262 if unsigned(parent) != 0: 5263 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type) 5264 while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)): 5265 elt = parent 5266 5267 # avoid using GetValueFromAddress 5268 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1 5269 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr))) 5270 parent = cast(parent, element_type) 5271 5272 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type) 5273 5274 # avoid using GetValueFromAddress 5275 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1 5276 elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr))) 5277 elt = cast(elt, element_type) 5278 5279 5280@lldb_command("showmaprb") 5281def ShowMapRB(cmd_args=None): 5282 """Routine to print out a VM map's RB tree 5283 usage: showmaprb <vm_map> 5284 """ 5285 if cmd_args == None or len(cmd_args) < 1: 5286 print "Invalid argument.", ShowMapRB.__doc__ 5287 return 5288 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 5289 print GetVMMapSummary.header 5290 print GetVMMapSummary(map_val) 5291 vme_rb_root = map_val.hdr.rb_head_store 5292 vme_ptr_type = GetType('struct vm_map_entry *') 5293 print GetVMEntrySummary.header 5294 for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'): 5295 print GetVMEntrySummary(vme) 5296 return None 5297 5298@lldb_command('show_all_owned_objects', 'T') 5299def ShowAllOwnedObjects(cmd_args=None, cmd_options={}): 5300 """ Routine to print the list of VM objects owned by each task 5301 -T: show only ledger-tagged objects 5302 """ 5303 showonlytagged = False 5304 if "-T" in cmd_options: 5305 showonlytagged = True 5306 for task in kern.tasks: 5307 ShowTaskOwnedVmObjects(task, showonlytagged) 5308 5309@lldb_command('show_task_owned_objects', 'T') 5310def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}): 5311 """ Routine to print the list of VM objects owned by the specified task 5312 -T: show only ledger-tagged objects 5313 """ 5314 showonlytagged = False 5315 if "-T" in cmd_options: 5316 showonlytagged = True 5317 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 5318 ShowTaskOwnedVmObjects(task, showonlytagged) 5319 5320@lldb_command('showdeviceinfo', 'J') 5321def ShowDeviceInfo(cmd_args=None, cmd_options={}): 5322 """ Routine to show basic device information (model, build, ncpus, etc...) 5323 Usage: memstats [-J] 5324 -J : Output json 5325 """ 5326 print_json = False 5327 if "-J" in cmd_options: 5328 print_json = True 5329 device_info = {} 5330 device_info["build"] = str(kern.globals.osversion) 5331 device_info["memoryConfig"] = int(kern.globals.max_mem_actual) 5332 device_info["ncpu"] = int(kern.globals.ncpu) 5333 device_info["pagesize"] = int(kern.globals.page_size) 5334 device_info["mlockLimit"] = long(kern.globals.vm_global_user_wire_limit) 5335 # Serializing to json here ensure we always catch bugs preventing 5336 # serialization 5337 as_json = json.dumps(device_info) 5338 5339 5340 if print_json: 5341 print as_json 5342 else: 5343 PrettyPrintDictionary(device_info) 5344 5345def ShowTaskOwnedVmObjects(task, showonlytagged=False): 5346 """ Routine to print out a summary listing of all the entries in a vm_map 5347 params: 5348 task - core.value : a object of type 'task *' 5349 returns: 5350 None 5351 """ 5352 taskobjq_total = lambda:None 5353 taskobjq_total.objects = 0 5354 taskobjq_total.vsize = 0 5355 taskobjq_total.rsize = 0 5356 taskobjq_total.wsize = 0 5357 taskobjq_total.csize = 0 5358 vmo_list_head = task.task_objq 5359 vmo_ptr_type = GetType('vm_object *') 5360 idx = 0 5361 for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"): 5362 idx += 1 5363 if not showonlytagged or vmo.vo_ledger_tag != 0: 5364 if taskobjq_total.objects == 0: 5365 print ' \n' 5366 print GetTaskSummary.header + ' ' + GetProcSummary.header 5367 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *')) 5368 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>2s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tg","owner","pid","process") 5369 ShowOwnedVmObject(vmo, idx, 0, taskobjq_total) 5370 if taskobjq_total.objects != 0: 5371 print " total:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(taskobjq_total.objects, taskobjq_total.vsize, taskobjq_total.rsize, taskobjq_total.wsize, taskobjq_total.csize) 5372 return None 5373 5374def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total): 5375 """ Routine to print out a VM object owned by a task 5376 params: 5377 object - core.value : a object of type 'struct vm_object *' 5378 returns: 5379 None 5380 """ 5381 page_size = kern.globals.page_size 5382 if object.purgable == 0: 5383 purgable = "N" 5384 elif object.purgable == 1: 5385 purgable = "V" 5386 elif object.purgable == 2: 5387 purgable = "E" 5388 elif object.purgable == 3: 5389 purgable = "D" 5390 else: 5391 purgable = "?" 5392 if object.pager == 0: 5393 compressed_count = 0 5394 else: 5395 compressor_pager = Cast(object.pager, 'compressor_pager *') 5396 compressed_count = compressor_pager.cpgr_num_slots_occupied 5397 5398 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>2d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner)) 5399 5400 taskobjq_total.objects += 1 5401 taskobjq_total.vsize += object.vo_un1.vou_size/page_size 5402 taskobjq_total.rsize += object.resident_page_count 5403 taskobjq_total.wsize += object.wired_page_count 5404 taskobjq_total.csize += compressed_count 5405 5406def GetProcPIDForObjectOwner(owner): 5407 """ same as GetProcPIDForTask() but deals with -1 for a disowned object 5408 """ 5409 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)): 5410 return -1 5411 return GetProcPIDForTask(owner) 5412 5413def GetProcNameForObjectOwner(owner): 5414 """ same as GetProcNameForTask() but deals with -1 for a disowned object 5415 """ 5416 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)): 5417 return "<disowned>" 5418 return GetProcNameForTask(owner) 5419 5420def GetDescForNamedEntry(mem_entry): 5421 out_str = "\n" 5422 out_str += "\t\tmem_entry {:#08x} ref:{:d} offset:{:#08x} size:{:#08x} prot{:d} backing {:#08x}".format(mem_entry, mem_entry.ref_count, mem_entry.offset, mem_entry.size, mem_entry.protection, mem_entry.backing.copy) 5423 if mem_entry.is_sub_map: 5424 out_str += " is_sub_map" 5425 elif mem_entry.is_copy: 5426 out_str += " is_copy" 5427 elif mem_entry.is_object: 5428 out_str += " is_object" 5429 else: 5430 out_str += " ???" 5431 return out_str 5432