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