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