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