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