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