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