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