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