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