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 if(page.vmp_object): 3304 m_object_val = _vm_page_unpack_ptr(page.vmp_object) 3305 object = kern.GetValueFromAddress(m_object_val, 'vm_object_t') 3306 if not object: 3307 print("No valid object: {:s}".format(m_object_val)) 3308 3309 pager=object.pager 3310 paging_in_progress=object.paging_in_progress 3311 activity_in_progress=object.activity_in_progress 3312 3313 print(ShowVMPage.header) 3314 print("{:>#20x} {:>15d} {:>15d} {:>15d} {:>20d} {:>#20x} {:>#20x} {:>#20x} {:>20d} {:>15d}".format( 3315 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)) 3316 3317 if show_object_info: 3318 print("\nPrinting object info for given page",object) 3319 showvmobject(object, 0, 0, 1, 1) 3320 3321 if show_callchain: 3322 print("Searching for Page: ",hex(page)) 3323 show_call_chain(hex(page), O) 3324 3325 print("Searching for object: ",hex(m_object_val)) 3326 show_call_chain(hex(m_object_val),O) 3327 3328 print("Searching for Pager: ",hex(pager)) 3329 show_call_chain(hex(pager),O) 3330 3331@lldb_command("showvmobject", "A:B:PRSTU") 3332def ShowVMObject(cmd_args=None, cmd_options={}): 3333 """Routine to print out a VM object and its shadow chain 3334 usage: showvmobject <vm_object> [-S] [-P] 3335 -S: show VM object shadow chain 3336 -P: show pager info (mapped file, compressed pages, ...) 3337 -U: show UPL info 3338 """ 3339 if cmd_args is None or len(cmd_args) == 0: 3340 raise ArgumentError() 3341 3342 show_pager_info = False 3343 show_all_shadows = False 3344 show_upl_info = False 3345 3346 if "-P" in cmd_options: 3347 show_pager_info = True 3348 if "-S" in cmd_options: 3349 show_all_shadows = True 3350 if "-U" in cmd_options: 3351 show_upl_info = True 3352 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 3353 showvmobject(object, 0, 0, show_pager_info, show_all_shadows, show_upl_info) 3354 3355def PrintUPLSummary(upl, spacing=''): 3356 indented_spacing = spacing + " "*4 3357 3358 page_size = kern.globals.page_size 3359 print(f"{spacing} {VT.Bold}{'Address (upl_t)':<18} {'Creator (thread)':<18} {'# pages':<10} {'associated UPL'}{VT.EndBold}") 3360 3361 num_pages = math.ceil(upl.u_size / page_size) 3362 associated_upl = f'{upl.associated_upl:#018x}' if upl.associated_upl else '' 3363 print(f'{spacing} {upl:#018x} {upl.upl_creator:#018x} {num_pages:<8} {associated_upl}') 3364 3365 first_page_info = True 3366 for page_ind in range(num_pages): 3367 if first_page_info: 3368 print(f"{indented_spacing} {VT.Bold}{'upl_index':<12} {'Address (upl_page_info *)':<28} {'ppnum':<24} {'page (vm_page_t)':<28}{VT.EndBold}") 3369 first_page_info = False 3370 3371 # lite_list is a bitfield marking pages locked by UPL 3372 bits_per_element = sizeof(upl.lite_list[0]) 3373 bitfield_number = int(page_ind / bits_per_element) 3374 bit_in_bitfield = (1 << (page_ind % bits_per_element)) 3375 if upl.lite_list[bitfield_number] & (bit_in_bitfield): 3376 upl_page_info = upl.page_list[page_ind] 3377 ppnum = upl_page_info.phys_addr 3378 page = _vm_page_get_page_from_phys(ppnum) 3379 page_addr = '' if page is None else f'{unsigned(addressof(page)):<#28x}' 3380 3381 print(f"{indented_spacing} {page_ind:<12} {unsigned(addressof(upl_page_info)):<#28x} {ppnum:<#24x} {page_addr}") 3382 3383def PrintVMObjUPLs(uplq_head): 3384 spacing = " "*19 3385 for upl in IterateQueue(addressof(uplq_head), 'upl_t', 'uplq'): 3386 PrintUPLSummary(upl, spacing) 3387 3388def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False, show_upl_info=False): 3389 page_size = kern.globals.page_size 3390 vnode_pager_ops = kern.globals.vnode_pager_ops 3391 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops)) 3392 depth = 0 3393 if size == 0 and object != 0 and object.internal: 3394 size = object.vo_un1.vou_size 3395 while object != 0: 3396 depth += 1 3397 if not show_all_shadows and depth != 1 and object.shadow != 0: 3398 offset += unsigned(object.vo_un2.vou_shadow_offset) 3399 object = object.shadow 3400 continue 3401 if object.copy_strategy == 0: 3402 copy_strategy="N" 3403 elif object.copy_strategy == 2: 3404 copy_strategy="D" 3405 elif object.copy_strategy == 4: 3406 copy_strategy="S" 3407 elif object.copy_strategy == 6: 3408 copy_strategy="F"; 3409 else: 3410 copy_strategy=str(object.copy_strategy) 3411 if object.internal: 3412 internal = "internal" 3413 else: 3414 internal = "external" 3415 purgeable = "NVED"[int(object.purgable)] 3416 pager_string = "" 3417 if object.phys_contiguous: 3418 pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size)) 3419 pager = object.pager 3420 if show_pager_info and pager != 0: 3421 if object.internal: 3422 pager_string = pager_string + "-> compressed:{:d} ({:#018x})".format(GetCompressedPagesForObject(object), object.pager) 3423 elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr: 3424 vnode_pager = Cast(pager,'vnode_pager *') 3425 pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle) 3426 else: 3427 pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager) 3428 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)) 3429# 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) 3430 3431 if show_upl_info: 3432 PrintVMObjUPLs(object.uplq) 3433 3434 offset += unsigned(object.vo_un2.vou_shadow_offset) 3435 object = object.shadow 3436 3437def 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): 3438 rsize = GetResidentPageCount(map) 3439 print("{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift")) 3440 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)) 3441 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) 3442 3443def 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): 3444 print("{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift")) 3445 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)) 3446 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) 3447 3448def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree, entry_filter, show_upl_info): 3449 page_size = kern.globals.page_size 3450 if hasattr(kern.globals, 'compressor_object'): 3451 compressor_object = kern.globals.compressor_object 3452 else: 3453 compressor_object = -1; 3454 vme_list_head = maphdr.links 3455 vme_ptr_type = GetType('vm_map_entry *') 3456 print("{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")) 3457 last_end = unsigned(maphdr.links.start) 3458 skipped_entries = 0 3459 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order): 3460 links = vme.links 3461 vme_start = links.start 3462 vme_end = links.end 3463 vme_start_val = unsigned(vme_start) 3464 vme_end_val = unsigned(vme_end) 3465 if start_vaddr != 0 and end_vaddr != 0: 3466 if vme_start_val > end_vaddr: 3467 break 3468 if unsigned(vme_end) <= start_vaddr: 3469 last_end = vme_end_val 3470 skipped_entries = skipped_entries + 1 3471 continue 3472 if skipped_entries != 0: 3473 print("... skipped {:d} entries ...".format(skipped_entries)) 3474 skipped_entries = 0 3475 if entry_filter and not entry_filter(vme): 3476 continue 3477 if vme_start_val != last_end: 3478 print("{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme_start,(vme_start_val - last_end) // page_size)) 3479 last_end = vme_end_val 3480 size = vme_end_val - vme_start_val 3481 object = get_vme_object(vme) 3482 object_val = int(object) 3483 if object_val == 0: 3484 object_str = "{: <#018x}".format(object) 3485 elif vme.is_sub_map: 3486 object_str = None 3487 3488 if object_val == kern.globals.bufferhdr_map: 3489 object_str = "BUFFERHDR_MAP" 3490 elif object_val == kern.globals.mb_map: 3491 object_str = "MB_MAP" 3492 elif object_val == kern.globals.bsd_pageable_map: 3493 object_str = "BSD_PAGEABLE_MAP" 3494 elif object_val == kern.globals.ipc_kernel_map: 3495 object_str = "IPC_KERNEL_MAP" 3496 elif object_val == kern.globals.ipc_kernel_copy_map: 3497 object_str = "IPC_KERNEL_COPY_MAP" 3498 elif hasattr(kern.globals, 'io_submap') and object_val == kern.globals.io_submap: 3499 object_str = "IO_SUBMAP" 3500 elif hasattr(kern.globals, 'pgz_submap') and object_val == kern.globals.pgz_submap: 3501 object_str = "ZALLOC:PGZ" 3502 elif hasattr(kern.globals, 'compressor_map') and object_val == kern.globals.compressor_map: 3503 object_str = "COMPRESSOR_MAP" 3504 elif hasattr(kern.globals, 'g_kext_map') and object_val == kern.globals.g_kext_map: 3505 object_str = "G_KEXT_MAP" 3506 elif hasattr(kern.globals, 'vector_upl_submap') and object_val == kern.globals.vector_upl_submap: 3507 object_str = "VECTOR_UPL_SUBMAP" 3508 elif object_val == kern.globals.zone_meta_map: 3509 object_str = "ZALLOC:META" 3510 else: 3511 for i in range(0, int(GetEnumValue('zone_submap_idx_t', 'Z_SUBMAP_IDX_COUNT'))): 3512 if object_val == kern.globals.zone_submaps[i]: 3513 object_str = "ZALLOC:{:s}".format(GetEnumName('zone_submap_idx_t', i, 'Z_SUBMAP_IDX_')) 3514 break 3515 if object_str is None: 3516 object_str = "submap:{: <#018x}".format(object) 3517 else: 3518 if object_val == kern.globals.kernel_object_default: 3519 object_str = "KERNEL_OBJECT" 3520 elif hasattr(kern.globals, 'kernel_object_tagged') and object_val == kern.globals.kernel_object_tagged: 3521 object_str = "KERNEL_OBJECT_TAGGED" 3522 elif object_val == compressor_object: 3523 object_str = "COMPRESSOR_OBJECT" 3524 else: 3525 object_str = "{: <#018x}".format(object) 3526 offset = get_vme_offset(vme) 3527 tag = unsigned(vme.vme_alias) 3528 3529 vme_protection = vme.protection 3530 protection = "" 3531 if vme_protection & 0x1: 3532 protection +="r" 3533 else: 3534 protection += "-" 3535 if vme_protection & 0x2: 3536 protection += "w" 3537 else: 3538 protection += "-" 3539 if vme_protection & 0x4: 3540 protection += "x" 3541 else: 3542 protection += "-" 3543 3544 vme_max_protection = vme.max_protection 3545 max_protection = "" 3546 if vme_max_protection & 0x1: 3547 max_protection += "r" 3548 else: 3549 max_protection += "-" 3550 if vme_max_protection & 0x2: 3551 max_protection += "w" 3552 else: 3553 max_protection += "-" 3554 if vme_max_protection & 0x4: 3555 max_protection += "x" 3556 else: 3557 max_protection += "-" 3558 3559 vme_flags = "" 3560 if vme.is_sub_map: 3561 vme_flags += "s" 3562 if vme.needs_copy: 3563 vme_flags += "n" 3564 if vme.use_pmap: 3565 vme_flags += "p" 3566 if vme.wired_count: 3567 vme_flags += "w" 3568 if vme.used_for_jit: 3569 vme_flags += "j" 3570 if vme.vme_permanent: 3571 vme_flags += "!" 3572 try: 3573 if vme.used_for_tpro: 3574 vme_flags += "t" 3575 except AttributeError: 3576 pass 3577 3578 tagstr = "" 3579 if pmap == kern.globals.kernel_pmap: 3580 xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *') 3581 if xsite and xsite.site.flags & 0x0200: 3582 tagstr = ".{:<3d}".format(xsite.loadTag) 3583 rb_info = "" 3584 if show_rb_tree: 3585 rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent) 3586 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)) 3587 if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and object_val != 0: 3588 pass # object is already intialized 3589 else: 3590 object = 0 3591 showvmobject(object, offset, size, show_pager_info, show_all_shadows, show_upl_info) 3592 if start_vaddr != 0 or end_vaddr != 0: 3593 print("...") 3594 elif unsigned(maphdr.links.end) > last_end: 3595 print("{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end) // page_size)) 3596 return None 3597 3598def CountMapTags(map, tagcounts, slow): 3599 page_size = unsigned(kern.globals.page_size) 3600 vme_list_head = map.hdr.links 3601 vme_ptr_type = GetType('vm_map_entry *') 3602 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 3603 object = get_vme_object(vme) 3604 tag = vme.vme_alias 3605 if object == kern.globals.kernel_object_default or (hasattr(kern.globals, 'kernel_object_tagged') and object == kern.globals.kernel_object_tagged): 3606 count = 0 3607 if not slow: 3608 count = unsigned(vme.links.end - vme.links.start) // page_size 3609 else: 3610 addr = unsigned(vme.links.start) 3611 while addr < unsigned(vme.links.end): 3612 hash_id = _calc_vm_page_hash(object, addr) 3613 page_list = kern.globals.vm_page_buckets[hash_id].page_list 3614 page = _vm_page_unpack_ptr(page_list) 3615 while (page != 0): 3616 vmpage = kern.GetValueFromAddress(page, 'vm_page_t') 3617 if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))): 3618 if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0): 3619 count += 1 3620 break 3621 page = _vm_page_unpack_ptr(vmpage.vmp_next_m) 3622 addr += page_size 3623 tagcounts[tag] += count 3624 elif vme.is_sub_map: 3625 CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow) 3626 return None 3627 3628def CountWiredObject(object, tagcounts): 3629 tagcounts[unsigned(object.wire_tag)] += object.wired_page_count 3630 return None 3631 3632def GetKmodIDName(kmod_id): 3633 kmod_val = kern.globals.kmod 3634 for kmod in IterateLinkedList(kmod_val, 'next'): 3635 if (kmod.id == kmod_id): 3636 return "{:<50s}".format(kmod.name) 3637 return "??" 3638 3639FixedTags = { 3640 0: "VM_KERN_MEMORY_NONE", 3641 1: "VM_KERN_MEMORY_OSFMK", 3642 2: "VM_KERN_MEMORY_BSD", 3643 3: "VM_KERN_MEMORY_IOKIT", 3644 4: "VM_KERN_MEMORY_LIBKERN", 3645 5: "VM_KERN_MEMORY_OSKEXT", 3646 6: "VM_KERN_MEMORY_KEXT", 3647 7: "VM_KERN_MEMORY_IPC", 3648 8: "VM_KERN_MEMORY_STACK", 3649 9: "VM_KERN_MEMORY_CPU", 3650 10: "VM_KERN_MEMORY_PMAP", 3651 11: "VM_KERN_MEMORY_PTE", 3652 12: "VM_KERN_MEMORY_ZONE", 3653 13: "VM_KERN_MEMORY_KALLOC", 3654 14: "VM_KERN_MEMORY_COMPRESSOR", 3655 15: "VM_KERN_MEMORY_COMPRESSED_DATA", 3656 16: "VM_KERN_MEMORY_PHANTOM_CACHE", 3657 17: "VM_KERN_MEMORY_WAITQ", 3658 18: "VM_KERN_MEMORY_DIAG", 3659 19: "VM_KERN_MEMORY_LOG", 3660 20: "VM_KERN_MEMORY_FILE", 3661 21: "VM_KERN_MEMORY_MBUF", 3662 22: "VM_KERN_MEMORY_UBC", 3663 23: "VM_KERN_MEMORY_SECURITY", 3664 24: "VM_KERN_MEMORY_MLOCK", 3665 25: "VM_KERN_MEMORY_REASON", 3666 26: "VM_KERN_MEMORY_SKYWALK", 3667 27: "VM_KERN_MEMORY_LTABLE", 3668 28: "VM_KERN_MEMORY_HV", 3669 29: "VM_KERN_MEMORY_KALLOC_DATA", 3670 30: "VM_KERN_MEMORY_RETIRED", 3671 31: "VM_KERN_MEMORY_KALLOC_TYPE", 3672 32: "VM_KERN_MEMORY_TRIAGE", 3673 33: "VM_KERN_MEMORY_RECOUNT", 3674 34: "VM_KERN_MEMORY_TAG", 3675 35: "VM_KERN_MEMORY_EXCLAVES", 3676 255:"VM_KERN_MEMORY_ANY", 3677} 3678 3679def GetVMKernName(tag): 3680 """ returns the formatted name for a vmtag and 3681 the sub-tag for kmod tags. 3682 """ 3683 if tag in FixedTags: 3684 return (FixedTags[tag], "") 3685 site = kern.globals.vm_allocation_sites[tag] 3686 if site: 3687 if site.flags & 0x007F: 3688 cstr = addressof(site.subtotals[site.subtotalscount]) 3689 return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "") 3690 else: 3691 if site.flags & 0x0200: 3692 xsite = Cast(site,'OSKextAccount *') 3693 tagstr = ".{:<3d}".format(xsite.loadTag) 3694 return (GetKmodIDName(xsite.loadTag), tagstr); 3695 else: 3696 return (kern.Symbolicate(site), "") 3697 return ("", "") 3698 3699@SBValueFormatter.converter("vm_kern_tag") 3700def vm_kern_tag_conversion(tag): 3701 s, tagstr = GetVMKernName(tag) 3702 3703 if tagstr != '': 3704 return "{} ({}{})".format(s.strip(), tag, tagstr) 3705 if s != '': 3706 return "{} ({})".format(s.strip(), tag) 3707 return str(tag) 3708 3709@lldb_command("showvmtags", "ASJO") 3710def showvmtags(cmd_args=None, cmd_options={}): 3711 """Routine to print out info about kernel wired page allocations 3712 usage: showvmtags 3713 iterates kernel map and vm objects totaling allocations by tag. 3714 usage: showvmtags -S [-O] 3715 also iterates kernel object pages individually - slow. 3716 usage: showvmtags -A [-O] 3717 show all tags, even tags that have no wired count 3718 usage: showvmtags -J [-O] 3719 Output json 3720 3721 -O: list in increasing size order 3722 """ 3723 slow = False 3724 print_json = False 3725 if "-S" in cmd_options: 3726 slow = True 3727 all_tags = False 3728 if "-A" in cmd_options: 3729 all_tags = True 3730 if "-J" in cmd_options: 3731 print_json = True 3732 3733 page_size = unsigned(kern.globals.page_size) 3734 nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1 3735 tagcounts = [0] * nsites 3736 tagmapped = [0] * nsites 3737 3738 if kern.globals.vm_tag_active_update: 3739 for tag in range(nsites): 3740 site = kern.globals.vm_allocation_sites[tag] 3741 if site: 3742 tagcounts[tag] = unsigned(site.total) 3743 tagmapped[tag] = unsigned(site.mapped) 3744 else: 3745 queue_head = kern.globals.vm_objects_wired 3746 for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'): 3747 if object != kern.globals.kernel_object_default and ((not hasattr(kern.globals, 'kernel_object_tagged')) or object != kern.globals.kernel_object_tagged): 3748 CountWiredObject(object, tagcounts) 3749 3750 CountMapTags(kern.globals.kernel_map, tagcounts, slow) 3751 3752 total = 0 3753 totalmapped = 0 3754 tags = [] 3755 for tag in range(nsites): 3756 if all_tags or tagcounts[tag] or tagmapped[tag]: 3757 current = {} 3758 total += tagcounts[tag] 3759 totalmapped += tagmapped[tag] 3760 (sitestr, tagstr) = GetVMKernName(tag) 3761 current["name"] = sitestr 3762 current["size"] = tagcounts[tag] 3763 current["mapped"] = tagmapped[tag] 3764 current["tag"] = tag 3765 current["tagstr"] = tagstr 3766 current["subtotals"] = [] 3767 3768 site = kern.globals.vm_allocation_sites[tag] 3769 for sub in range(site.subtotalscount): 3770 alloctag = unsigned(site.subtotals[sub].tag) 3771 amount = unsigned(site.subtotals[sub].total) 3772 subsite = kern.globals.vm_allocation_sites[alloctag] 3773 if alloctag and subsite: 3774 (sitestr, tagstr) = GetVMKernName(alloctag) 3775 current["subtotals"].append({ 3776 "amount": amount, 3777 "flags": int(subsite.flags), 3778 "tag": alloctag, 3779 "tagstr": tagstr, 3780 "sitestr": sitestr, 3781 }) 3782 tags.append(current) 3783 3784 if "-O" in cmd_options: 3785 tags.sort(key = lambda tag: tag['size']) 3786 3787 # Serializing to json here ensure we always catch bugs preventing 3788 # serialization 3789 as_json = json.dumps(tags) 3790 if print_json: 3791 print(as_json) 3792 else: 3793 print(" vm_allocation_tag_highest: {:<7d} ".format(nsites - 1)) 3794 print(" {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "size", "mapped", "name")) 3795 for tag in tags: 3796 if not tagstr: 3797 tagstr = "" 3798 print(" {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag["tag"], tag["tagstr"], tag["size"] // 1024, tag["mapped"] // 1024, tag["name"])) 3799 for sub in tag["subtotals"]: 3800 if ((sub["flags"] & 0x007f) == 0): 3801 kind_str = "named" 3802 else: 3803 kind_str = "from" 3804 3805 print(" {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", sub["amount"] // 1024, kind_str, sub["tag"], sub["tagstr"], sub["sitestr"])) 3806 3807 print("Total: {:>7d}K {:>7d}K".format(total // 1024, totalmapped // 1024)) 3808 return None 3809 3810 3811def FindVMEntriesForVnode(task, vn): 3812 """ returns an array of vme that have the vnode set to defined vnode 3813 each entry in array is of format (vme, start_addr, end_address, protection) 3814 """ 3815 retval = [] 3816 vmmap = task.map 3817 pmap = vmmap.pmap 3818 pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops)) 3819 debuglog("pager_ops_addr %s" % hex(pager_ops_addr)) 3820 3821 if unsigned(pmap) == 0: 3822 return retval 3823 vme_list_head = vmmap.hdr.links 3824 vme_ptr_type = gettype('vm_map_entry *') 3825 for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'): 3826 #print vme 3827 if unsigned(vme.is_sub_map) == 0 and unsigned(get_vme_object(vme)) != 0: 3828 obj = get_vme_object(vme) 3829 else: 3830 continue 3831 3832 while obj != 0: 3833 if obj.pager != 0: 3834 if obj.internal: 3835 pass 3836 else: 3837 vn_pager = Cast(obj.pager, 'vnode_pager *') 3838 if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn): 3839 retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection))) 3840 obj = obj.shadow 3841 return retval 3842 3843@lldb_command('showtaskloadinfo') 3844def ShowTaskLoadInfo(cmd_args=None, cmd_options={}): 3845 """ Print the load address and uuid for the process 3846 Usage: (lldb)showtaskloadinfo <task_t> 3847 """ 3848 if cmd_args is None or len(cmd_args) == 0: 3849 raise ArgumentError("Insufficient arguments") 3850 3851 t = kern.GetValueFromAddress(cmd_args[0], 'struct task *') 3852 print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}" 3853 p = GetProcFromTask(t) 3854 if p is None: 3855 print("Task has no associated BSD process.") 3856 return 3857 uuid_out_string = GetUUIDSummary(p.p_uuid) 3858 filepath = GetVnodePath(p.p_textvp) 3859 libname = filepath.split('/')[-1] 3860 mappings = FindVMEntriesForVnode(t, p.p_textvp) 3861 load_addr = 0 3862 end_addr = 0 3863 for m in mappings: 3864 if m[3] == 5: 3865 load_addr = m[1] 3866 end_addr = m[2] 3867 print(print_format.format(load_addr, end_addr, 3868 libname, uuid_out_string, filepath)) 3869 3870@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object")) 3871@lldb_command('vmpagelookup') 3872def VMPageLookup(cmd_args=None): 3873 """ Print the pages in the page bucket corresponding to the provided object and offset. 3874 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t> 3875 """ 3876 if cmd_args is None or len(cmd_args) != 2: 3877 raise ArgumentError("Please specify an object and offset.") 3878 3879 format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n" 3880 3881 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long') 3882 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long') 3883 3884 hash_id = _calc_vm_page_hash(obj, off) 3885 3886 page_list = kern.globals.vm_page_buckets[hash_id].page_list 3887 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list))) 3888 3889 print(VMPageLookup.header) 3890 page = _vm_page_unpack_ptr(page_list) 3891 while page != 0: 3892 pg_t = kern.GetValueFromAddress(page, 'vm_page_t') 3893 print(format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object))) 3894 page = _vm_page_unpack_ptr(pg_t.vmp_next_m) 3895 3896 3897def _vm_page_get_phys_page(page): 3898 if kern.arch == 'x86_64': 3899 return page.vmp_phys_page 3900 3901 if page == 0 : 3902 return 0 3903 3904 m = unsigned(page) 3905 3906 if m >= unsigned(kern.globals.vm_pages) and m < unsigned(kern.globals.vm_pages_end) : 3907 return (m - unsigned(kern.globals.vm_pages)) // sizeof('struct vm_page') + unsigned(kern.globals.vm_pages_first_pnum) 3908 3909 target = LazyTarget.GetTarget() 3910 return target.xReadUInt32(unsigned(page) + sizeof('struct vm_page')) 3911 3912 3913@lldb_command('vmpage_get_phys_page') 3914def VmPageGetPhysPage(cmd_args=None): 3915 """ return the physical page for a vm_page_t 3916 usage: vm_page_get_phys_page <vm_page_t> 3917 """ 3918 if cmd_args is None or len(cmd_args) != 1: 3919 raise ArgumentError("Missing page argument") 3920 3921 target = LazyTarget.GetTarget() 3922 addr = ArgumentStringToAddress(cmd_args[0]) 3923 page = target.xCreateValueFromAddress(None, addr, gettype('struct vm_page')) 3924 phys_page = _vm_page_get_phys_page(value(page.AddressOf())) 3925 print("phys_page = {:#x}".format(phys_page)) 3926 3927 3928def _vm_page_get_page_from_phys(pnum): 3929 """ Attempt to return page struct from physical page address""" 3930 if kern.arch == 'x86_64': 3931 return None 3932 3933 pmap_first_pnum = unsigned(kern.globals.pmap_first_pnum) 3934 vm_pages_first_pnum = unsigned(kern.globals.vm_pages_first_pnum) 3935 vm_pages_count = unsigned(kern.globals.vm_pages_count) 3936 3937 if pmap_first_pnum <= pnum < vm_pages_first_pnum: 3938 target = LazyTarget.GetTarget() 3939 3940 radix = kern.globals.vm_pages_radix_root 3941 level = unsigned(radix) & 0x7 3942 node = unsigned(radix) - level 3943 index = pnum - pmap_first_pnum 3944 unpack = kmemory.KMem.get_shared().vm_page_packing.unpack 3945 3946 while node: 3947 key = (index >> (8 * level)) & 0xff 3948 node = unpack(target.xReadUInt32(node + key * 4)) 3949 if node and level == 0: 3950 name = "page_for_pnum_{:#x}".format(pnum) 3951 v = target.xCreateValueFromAddress(name, node, gettype('struct vm_page')) 3952 return value(v) 3953 level -= 1 3954 3955 return None 3956 3957 elif vm_pages_first_pnum <= pnum < vm_pages_first_pnum + vm_pages_count: 3958 return kern.globals.vm_pages[pnum - vm_pages_first_pnum] 3959 3960 return None 3961 3962 3963@lldb_command('vmpage_from_phys_page') 3964def VmPageFromPhysPage(cmd_args=None): 3965 """ return the vm_page_t for a physical page if possible 3966 usage: vm_page_from_phys_page <ppnum_t> 3967 """ 3968 3969 if cmd_args is None or len(cmd_args) != 1: 3970 raise ArgumentError("Missing pnum argument") 3971 3972 pnum = ArgumentStringToInt(cmd_args[0]) 3973 page = _vm_page_get_page_from_phys(pnum) 3974 if page is None: 3975 print("couldn't find page for pnum = {:#x}".format(pnum)) 3976 return 3977 3978 print(xnu_format( 3979 "page = (vm_page_t){:#x}\n" 3980 "\n" 3981 "{:s}", 3982 addressof(page), 3983 str(page))) 3984 3985 3986@lldb_command('vmpage_unpack_ptr') 3987def VmPageUnpackPtr(cmd_args=None): 3988 """ unpack a pointer 3989 usage: vm_page_unpack_ptr <packed_ptr> 3990 """ 3991 if cmd_args is None or len(cmd_args) == 0: 3992 print("Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help.") 3993 return 3994 3995 packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long') 3996 unpacked = _vm_page_unpack_ptr(packed) 3997 print("unpacked pointer = 0x%x\n" % unpacked) 3998 3999 4000def _vm_page_unpack_ptr(page: value) -> int: 4001 if kern.ptrsize == 4 : 4002 return int(page) 4003 4004 if page == 0 : 4005 return 0 4006 4007 params = kern.globals.vm_page_packing_params 4008 ptr_shift = params.vmpp_shift 4009 ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask 4010 4011 # when no mask and shift on 64bit systems, we're working with real/non-packed pointers 4012 if ptr_shift == 0 and ptr_mask == 0: 4013 return int(page) 4014 4015 if unsigned(page) & unsigned(ptr_mask): 4016 masked_page = (unsigned(page) & ~ptr_mask) 4017 # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge 4018 vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0])) 4019 element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr 4020 return (vm_pages_addr + masked_page * element_size) 4021 4022 kmem = kmemory.KMem.get_shared() 4023 return kmem.vm_page_packing.unpack(unsigned(page)) 4024 4025@lldb_command('calcvmpagehash') 4026def CalcVMPageHash(cmd_args=None): 4027 """ Get the page bucket corresponding to the provided object and offset. 4028 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t> 4029 """ 4030 if cmd_args is None or len(cmd_args) != 2: 4031 raise ArgumentError("Please specify an object and offset.") 4032 4033 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long') 4034 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long') 4035 4036 hash_id = _calc_vm_page_hash(obj, off) 4037 4038 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list))) 4039 return None 4040 4041def _calc_vm_page_hash(obj, off): 4042 bucket_hash = (int) (kern.globals.vm_page_bucket_hash) 4043 hash_mask = (int) (kern.globals.vm_page_hash_mask) 4044 4045 one = (obj * bucket_hash) & 0xFFFFFFFF 4046 two = off >> unsigned(kern.globals.page_shift) 4047 three = two ^ bucket_hash 4048 four = one + three 4049 hash_id = four & hash_mask 4050 4051 return hash_id 4052 4053#Macro: showallocatedzoneelement 4054@lldb_command('showallocatedzoneelement', fancy=True) 4055def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}, O=None): 4056 """ Show all the allocated elements in a zone 4057 usage: showzoneallocelements <address of zone> 4058 """ 4059 if len(cmd_args) < 1: 4060 raise ArgumentError("Please specify a zone") 4061 4062 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *') 4063 array = kern.GetGlobalVariable('zone_array') 4064 index = (unsigned(zone) - array.GetSBValue().GetLoadAddress()) // gettype('struct zone').GetByteSize() 4065 with O.table("{:<8s} {:<s}".format("Index", "Address")): 4066 i = 1 4067 for elem in kmemory.Zone(index): 4068 print(O.format("{:>8d} {:#x}", i, elem)) 4069 i += 1 4070 4071#EndMacro: showallocatedzoneelement 4072 4073def match_vm_page_attributes(page, matching_attributes): 4074 page_ptr = addressof(page) 4075 unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object) 4076 matched_attributes = 0 4077 if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]): 4078 matched_attributes += 1 4079 if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])): 4080 matched_attributes += 1 4081 if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])): 4082 matched_attributes += 1 4083 if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])): 4084 matched_attributes += 1 4085 if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1: 4086 matched_attributes += 1 4087 4088 return matched_attributes 4089 4090#Macro scan_vm_pages 4091@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")) 4092@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA') 4093def ScanVMPages(cmd_args=None, cmd_options={}): 4094 """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes. 4095 usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone] 4096 4097 scan_vm_pages -A: scan vm pages in the global vm_pages array 4098 scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone 4099 scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue 4100 scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object 4101 scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value 4102 scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number 4103 scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set 4104 scan_vm_pages <-A> -I <start_index>: Start the scan from start_index 4105 scan_vm_pages <-A> -N <npages>: Scan at most npages 4106 """ 4107 if (len(cmd_options) < 1): 4108 raise ArgumentError("Please specify at least one matching attribute") 4109 4110 vm_pages = kern.globals.vm_pages 4111 vm_pages_count = kern.globals.vm_pages_count 4112 4113 start_index = 0 4114 npages = vm_pages_count 4115 scan_vmpages_array = False 4116 scan_vmpages_zone = False 4117 attribute_count = 0 4118 4119 if "-A" in cmd_options: 4120 scan_vmpages_array = True 4121 4122 if "-Z" in cmd_options: 4123 scan_vmpages_zone = True 4124 4125 if not scan_vmpages_array and not scan_vmpages_zone: 4126 raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)") 4127 4128 attribute_values = {} 4129 if "-S" in cmd_options: 4130 attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int') 4131 attribute_count += 1 4132 4133 if "-O" in cmd_options: 4134 attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t') 4135 attribute_count += 1 4136 4137 if "-F" in cmd_options: 4138 attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long') 4139 attribute_count += 1 4140 4141 if "-P" in cmd_options: 4142 attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int') 4143 attribute_count += 1 4144 4145 if "-B" in cmd_options: 4146 valid_vmp_bitfields = [ 4147 "vmp_on_specialq", 4148 "vmp_canonical", 4149 "vmp_gobbled", 4150 "vmp_laundry", 4151 "vmp_no_cache", 4152 "vmp_reference", 4153 "vmp_busy", 4154 "vmp_wanted", 4155 "vmp_tabled", 4156 "vmp_hashed", 4157 "vmp_clustered", 4158 "vmp_pmapped", 4159 "vmp_xpmapped", 4160 "vmp_free_when_done", 4161 "vmp_absent", 4162 "vmp_error", 4163 "vmp_dirty", 4164 "vmp_cleaning", 4165 "vmp_precious", 4166 "vmp_overwriting", 4167 "vmp_restart", 4168 "vmp_unusual", 4169 "vmp_cs_validated", 4170 "vmp_cs_tainted", 4171 "vmp_cs_nx", 4172 "vmp_reusable", 4173 "vmp_lopage", 4174 "vmp_written_by_kernel", 4175 "vmp_unused_object_bits" 4176 ] 4177 attribute_values["bitfield"] = cmd_options["-B"] 4178 if attribute_values["bitfield"] in valid_vmp_bitfields: 4179 attribute_count += 1 4180 else: 4181 raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield)) 4182 4183 if "-I" in cmd_options: 4184 start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int') 4185 npages = vm_pages_count - start_index 4186 4187 if "-N" in cmd_options: 4188 npages = kern.GetValueFromAddress(cmd_options["-N"], 'int') 4189 if npages == 0: 4190 raise ArgumentError("You specified -N 0, nothing to be scanned") 4191 4192 end_index = start_index + npages - 1 4193 if end_index >= vm_pages_count: 4194 raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count)) 4195 4196 header_after_n_lines = 40 4197 format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}" 4198 4199 found_in_array = 0 4200 if scan_vmpages_array: 4201 print("Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count)) 4202 i = start_index 4203 while i <= end_index: 4204 page = vm_pages[i] 4205 if match_vm_page_attributes(page, attribute_values) == attribute_count: 4206 if found_in_array % header_after_n_lines == 0: 4207 print(ScanVMPages.header) 4208 4209 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)))) 4210 found_in_array += 1 4211 4212 i += 1 4213 4214 found_in_zone = 0 4215 if scan_vmpages_zone: 4216 page_size = kern.GetGlobalVariable('page_size') 4217 print("Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count)) 4218 4219 print("Scanning page queues in the vm_pages zone...") 4220 for elem in kmemory.Zone('vm pages'): 4221 page = kern.GetValueFromAddress(elem, 'vm_page_t') 4222 4223 if match_vm_page_attributes(page, attribute_values) == attribute_count: 4224 if found_in_zone % header_after_n_lines == 0: 4225 print(ScanVMPages.header) 4226 4227 vm_object = _vm_page_unpack_ptr(page.vmp_object) 4228 phys_page = _vm_page_get_phys_page(page) 4229 print(format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page)) 4230 found_in_zone += 1 4231 4232 total = found_in_array + found_in_zone 4233 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)) 4234 4235#EndMacro scan_vm_pages 4236 4237VM_PAGE_IS_WIRED = 1 4238 4239@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")) 4240@lldb_command('vmobjectwalkpages', 'CSBNQP:O:') 4241def VMObjectWalkPages(cmd_args=None, cmd_options={}): 4242 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we 4243 specifically look for this page, highlighting it in the output or noting if it was not found. For 4244 each page, we confirm that it points to the object. We also keep track of the number of pages we 4245 see and compare this to the object's resident page count field. 4246 Usage: 4247 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default) 4248 vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages 4249 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 4250 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit 4251 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) 4252 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with *** 4253 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page 4254 vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset 4255 4256 """ 4257 4258 if (cmd_args is None or len(cmd_args) < 1): 4259 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t") 4260 4261 out_string = "" 4262 4263 obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 4264 4265 page = 0 4266 if "-P" in cmd_options: 4267 page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t') 4268 4269 off = -1 4270 if "-O" in cmd_options: 4271 off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t') 4272 4273 stop = 0 4274 if "-S" in cmd_options: 4275 if page == 0 and off < 0: 4276 raise ArgumentError("-S can only be passed when a page is specified with -P or -O") 4277 stop = 1 4278 4279 walk_backwards = False 4280 if "-B" in cmd_options: 4281 walk_backwards = True 4282 4283 quiet_mode = False 4284 if "-Q" in cmd_options: 4285 quiet_mode = True 4286 4287 if not quiet_mode: 4288 print(VMObjectWalkPages.header) 4289 format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t" 4290 first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}\t" 4291 second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:" 4292 second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:" 4293 second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:" 4294 second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}" 4295 4296 limit = 4096 #arbitrary limit of number of pages to walk 4297 ignore_limit = 0 4298 if "-N" in cmd_options: 4299 ignore_limit = 1 4300 4301 show_compressed = 0 4302 if "-C" in cmd_options: 4303 show_compressed = 1 4304 4305 page_count = 0 4306 res_page_count = unsigned(obj.resident_page_count) 4307 page_found = False 4308 pages_seen = set() 4309 4310 for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr): 4311 page_count += 1 4312 out_string = "" 4313 if (page != 0 and not(page_found) and vmp == page): 4314 out_string += "******" 4315 page_found = True 4316 4317 if (off > 0 and not(page_found) and vmp.vmp_offset == off): 4318 out_string += "******" 4319 page_found = True 4320 4321 if page != 0 or off > 0 or quiet_mode: 4322 if (page_count % 1000) == 0: 4323 print("traversed %d pages ...\n" % (page_count)) 4324 else: 4325 phys_page = _vm_page_get_phys_page(vmp) 4326 vmp_fictitious = phys_page in (0xfffffffe, 0xffffffff) 4327 vmp_private = not vmp.vmp_canonical and not vmp_fictitious 4328 4329 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) 4330 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, 4331 vmp_private, vmp.vmp_reference) 4332 4333 if hasattr(vmp,'slid'): 4334 vmp_slid = vmp.slid 4335 else: 4336 vmp_slid = 0 4337 out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp_fictitious, vmp.vmp_clustered, 4338 vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent, 4339 vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting, 4340 vmp.vmp_restart, vmp.vmp_unusual, 0, 0, 4341 vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid, 4342 vmp.vmp_written_by_kernel) 4343 4344 if (vmp in pages_seen): 4345 print(out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n") 4346 return 4347 4348 if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)): 4349 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)))) 4350 return 4351 4352 if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0): 4353 print(out_string + " page in wired state with wire_count of 0\n") 4354 print("vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n") 4355 print("stopping...\n") 4356 return 4357 4358 if (hasattr(vmp, 'vmp_unused_page_bits') and (vmp.vmp_unused_page_bits != 0)): 4359 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)) 4360 print("stopping...\n") 4361 return 4362 4363 if (hasattr(vmp, 'vmp_unused_object_bits') and (vmp.vmp_unused_object_bits != 0)): 4364 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)) 4365 print("stopping...\n") 4366 return 4367 4368 pages_seen.add(vmp) 4369 4370 if False: 4371 hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset) 4372 hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list 4373 hash_page = _vm_page_unpack_ptr(hash_page_list) 4374 hash_page_t = 0 4375 4376 while (hash_page != 0): 4377 hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t') 4378 if hash_page_t == vmp: 4379 break 4380 hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m) 4381 4382 if (unsigned(vmp) != unsigned(hash_page_t)): 4383 print(out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n") 4384 print(lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))) 4385 return 4386 4387 if (page_count >= limit and not(ignore_limit)): 4388 print(out_string + "Limit reached (%d pages), stopping..." % (limit)) 4389 break 4390 4391 print(out_string) 4392 4393 if page_found and stop: 4394 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))) 4395 return 4396 4397 if (page != 0): 4398 print("page found? : %s\n" % page_found) 4399 4400 if (off > 0): 4401 print("page found? : %s\n" % page_found) 4402 4403 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))) 4404 4405 if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)): 4406 pager = Cast(obj.pager, 'compressor_pager *') 4407 chunks = pager.cpgr_num_slots // 128 4408 pagesize = kern.globals.page_size 4409 4410 page_idx = 0 4411 while page_idx < pager.cpgr_num_slots: 4412 if chunks != 0: 4413 chunk = pager.cpgr_slots.cpgr_islots[page_idx // 128] 4414 slot = chunk[page_idx % 128] 4415 elif pager.cpgr_num_slots > 2: 4416 slot = pager.cpgr_slots.cpgr_dslots[page_idx] 4417 else: 4418 slot = pager.cpgr_slots.cpgr_eslots[page_idx] 4419 4420 if slot != 0: 4421 print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot)) 4422 page_idx = page_idx + 1 4423 4424 4425@lldb_command("show_all_apple_protect_pagers") 4426def ShowAllAppleProtectPagers(cmd_args=None): 4427 """Routine to print all apple_protect pagers 4428 usage: show_all_apple_protect_pagers 4429 """ 4430 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")) 4431 qhead = kern.globals.apple_protect_pager_queue 4432 qtype = GetType('apple_protect_pager *') 4433 qcnt = kern.globals.apple_protect_pager_count 4434 idx = 0 4435 for pager in IterateQueue(qhead, qtype, "pager_queue"): 4436 idx = idx + 1 4437 show_apple_protect_pager(pager, qcnt, idx) 4438 4439@lldb_command("show_apple_protect_pager") 4440def ShowAppleProtectPager(cmd_args=None): 4441 """Routine to print out info about an apple_protect pager 4442 usage: show_apple_protect_pager <pager> 4443 """ 4444 if cmd_args is None or len(cmd_args) == 0: 4445 raise ArgumentError() 4446 4447 pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t') 4448 show_apple_protect_pager(pager, 1, 1) 4449 4450def show_apple_protect_pager(pager, qcnt, idx): 4451 object = pager.backing_object 4452 shadow = object.shadow 4453 while shadow != 0: 4454 object = shadow 4455 shadow = object.shadow 4456 vnode_pager = Cast(object.pager,'vnode_pager *') 4457 filename = GetVnodePath(vnode_pager.vnode_handle) 4458 if hasattr(pager, "ap_pgr_hdr_ref"): 4459 refcnt = pager.ap_pgr_hdr_ref 4460 else: 4461 refcnt = pager.ap_pgr_hdr.mo_ref 4462 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)) 4463 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) 4464 4465@lldb_command("show_all_shared_region_pagers") 4466def ShowAllSharedRegionPagers(cmd_args=None): 4467 """Routine to print all shared_region pagers 4468 usage: show_all_shared_region_pagers 4469 """ 4470 print("{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "offset", "jop_key", "slide", "slide_info")) 4471 qhead = kern.globals.shared_region_pager_queue 4472 qtype = GetType('shared_region_pager *') 4473 qcnt = kern.globals.shared_region_pager_count 4474 idx = 0 4475 for pager in IterateQueue(qhead, qtype, "srp_queue"): 4476 idx = idx + 1 4477 show_shared_region_pager(pager, qcnt, idx) 4478 4479@lldb_command("show_shared_region_pager") 4480def ShowSharedRegionPager(cmd_args=None): 4481 """Routine to print out info about a shared_region pager 4482 usage: show_shared_region_pager <pager> 4483 """ 4484 if cmd_args is None or len(cmd_args) == 0: 4485 raise ArgumentError() 4486 4487 pager = kern.GetValueFromAddress(cmd_args[0], 'shared_region_pager_t') 4488 show_shared_region_pager(pager, 1, 1) 4489 4490def show_shared_region_pager(pager, qcnt, idx): 4491 object = pager.srp_backing_object 4492 shadow = object.shadow 4493 while shadow != 0: 4494 object = shadow 4495 shadow = object.shadow 4496 vnode_pager = Cast(object.pager,'vnode_pager *') 4497 filename = GetVnodePath(vnode_pager.vnode_handle) 4498 if hasattr(pager, 'srp_ref_count'): 4499 ref_count = pager.srp_ref_count 4500 else: 4501 ref_count = pager.srp_header.mo_ref 4502 if hasattr(pager, 'srp_jop_key'): 4503 jop_key = pager.srp_jop_key 4504 else: 4505 jop_key = -1 4506 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)) 4507 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) 4508 4509@lldb_command("show_all_dyld_pagers") 4510def ShowAllDyldPagers(cmd_args=None): 4511 """Routine to print all dyld pagers 4512 usage: show_all_dyld_pagers 4513 """ 4514 print(ShowDyldPager.header) 4515 qhead = kern.globals.dyld_pager_queue 4516 qtype = GetType('dyld_pager *') 4517 qcnt = kern.globals.dyld_pager_count 4518 idx = 0 4519 for pager in IterateQueue(qhead, qtype, "dyld_pager_queue"): 4520 idx = idx + 1 4521 show_dyld_pager(pager, qcnt, idx) 4522 4523@header("{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "link_info", "link_info_size")) 4524@lldb_command("show_dyld_pager") 4525def ShowDyldPager(cmd_args=None): 4526 """Routine to print out info about a dyld pager 4527 usage: show_dyld_pager <pager> 4528 """ 4529 if cmd_args is None or len(cmd_args) == 0: 4530 raise ArgumentError() 4531 4532 print(ShowDyldPager.header) 4533 pager = kern.GetValueFromAddress(cmd_args[0], 'dyld_pager_t') 4534 show_dyld_pager(pager, 1, 1) 4535 4536def show_dyld_pager(pager, qcnt, idx): 4537 object = pager.dyld_backing_object 4538 shadow = object.shadow 4539 while shadow != 0: 4540 object = shadow 4541 shadow = object.shadow 4542 vnode_pager = Cast(object.pager,'vnode_pager *') 4543 filename = GetVnodePath(vnode_pager.vnode_handle) 4544 ref_count = pager.dyld_header.mo_ref 4545 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)) 4546 show_dyld_pager_regions(pager) 4547 print("\tvnode:{: <#018x} {:s}\n".format(vnode_pager.vnode_handle, filename)) 4548 showvmobject(pager.dyld_backing_object, show_pager_info=True, show_all_shadows=True, show_upl_info=True) 4549 4550def show_dyld_pager_regions(pager): 4551 """Routine to print out region info about a dyld pager 4552 """ 4553 print("\tregions:") 4554 print("\t\t{:>3s}/{:<3s} {:<18s} {:<18s} {:<18s}".format("#", "#", "file_offset", "address", "size")) 4555 for idx in range(pager.dyld_num_range): 4556 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])) 4557 4558@lldb_command("show_console_ring") 4559def ShowConsoleRingData(cmd_args=None): 4560 """ Print console ring buffer stats and data 4561 """ 4562 cr = kern.globals.console_ring 4563 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)) 4564 pending_data = [] 4565 for i in range(unsigned(cr.used)): 4566 idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len) 4567 pending_data.append("{:c}".format(cr.buffer[idx])) 4568 4569 if pending_data: 4570 print("Data:") 4571 print("".join(pending_data)) 4572 4573def ShowJetsamZprintSnapshot(): 4574 """ Helper function for the ShowJetsamSnapshot lldb command to print the last Jetsam zprint snapshot 4575 """ 4576 jzs_trigger_band = kern.GetGlobalVariable('jzs_trigger_band') 4577 jzs_gencount = kern.GetGlobalVariable('jzs_gencount') 4578 jzs_names = kern.globals.jzs_names 4579 jzs_info = kern.globals.jzs_info 4580 4581 if (unsigned(jzs_gencount) == ((1 << 64) - 1)): 4582 print("No jetsam zprint snapshot found\n") 4583 return 4584 print("Jetsam zprint snapshot jzs_trigger_band: {:2d}\n".format(jzs_trigger_band)) 4585 4586 info_hdr = "{0: >3s} {1: <30s} {2: >6s} {3: >11s}" 4587 info_fmt = "{0: >3d} {1: <30s} {2: >6d} {3: >11d}" 4588 4589 count = kern.GetGlobalVariable('jzs_zone_cnt') 4590 4591 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)) 4592 print(info_hdr.format("", "", "elem", "cur")) 4593 print(info_hdr.format("#", "name", "size", "inuse")) 4594 print("-----------------------------------------------------------------------------------------------------------") 4595 idx = 0; 4596 while idx < count: 4597 info = dereference(Cast(addressof(jzs_info[idx]), 'mach_zone_info_t *')) 4598 if info.mzi_elem_size > 0: 4599 print(info_fmt.format(idx, jzs_names[idx].mzn_name, info.mzi_elem_size, info.mzi_count)) 4600 idx += 1 4601 4602 jzs_meminfo = kern.globals.jzs_meminfo 4603 4604 count = kern.GetGlobalVariable('jzs_meminfo_cnt') 4605 print("\nJetsam zprint snapshot: wired memory info when jetsam last hit band: {:2d}, jzs_gencount: {:6d}\n\n".format(jzs_trigger_band, jzs_gencount)) 4606 print(" {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "peak", "size", "name")) 4607 print("---------------------------------------------------------------------------------------------------") 4608 idx = 0; 4609 while idx < count: 4610 meminfo = dereference(Cast(addressof(jzs_meminfo[idx]), 'mach_memory_info_t *')) 4611 peak = unsigned(meminfo.peak / 1024) 4612 size = unsigned(meminfo.size / 1024) 4613 if peak > 0: 4614 tag = unsigned(meminfo.tag) 4615 (sitestr, tagstr) = GetVMKernName(tag) 4616 print(" {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag, tagstr, peak, size, sitestr)) 4617 idx += 1 4618 4619 return 4620 4621# Macro: showjetsamsnapshot 4622 4623@lldb_command("showjetsamsnapshot", "DA") 4624def ShowJetsamSnapshot(cmd_args=None, cmd_options={}): 4625 """ Dump entries in the jetsam snapshot table 4626 usage: showjetsamsnapshot [-D] [-A] 4627 Use -D flag to print extra physfootprint details 4628 Use -A flag to print all entries (regardless of valid count) 4629 """ 4630 4631 # Not shown are uuid, user_data, cpu_time 4632 4633 global kern 4634 4635 show_footprint_details = False 4636 show_all_entries = False 4637 4638 if "-D" in cmd_options: 4639 show_footprint_details = True 4640 4641 if "-A" in cmd_options: 4642 show_all_entries = True 4643 4644 valid_count = kern.globals.memorystatus_jetsam_snapshot_count 4645 max_count = kern.globals.memorystatus_jetsam_snapshot_max 4646 4647 if show_all_entries: 4648 count = max_count 4649 else: 4650 count = valid_count 4651 4652 print("{:s}".format(valid_count)) 4653 print("{:s}".format(max_count)) 4654 4655 if int(count) == 0: 4656 print("The jetsam snapshot is empty.") 4657 print("Use -A to force dump all entries (regardless of valid count)") 4658 return 4659 4660 # Dumps the snapshot header info 4661 print(lldb_run_command('p *memorystatus_jetsam_snapshot')) 4662 4663 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}" 4664 if show_footprint_details: 4665 hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}" 4666 4667 4668 if not show_footprint_details: 4669 print(hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')) 4670 print(hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')) 4671 else: 4672 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')) 4673 print(hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')) 4674 4675 4676 entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\ 4677 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\ 4678 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\ 4679 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\ 4680 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}" 4681 4682 if show_footprint_details: 4683 entry_format += "{e.jse_internal_pages: >15d} "\ 4684 "{e.jse_internal_compressed_pages: >15d} "\ 4685 "{e.jse_iokit_mapped_pages: >12d} "\ 4686 "{e.jse_purgeable_nonvolatile_pages: >12d} "\ 4687 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\ 4688 "{e.jse_alternate_accounting_pages: >10d} "\ 4689 "{e.jse_alternate_accounting_compressed_pages: >13d} "\ 4690 "{e.jse_page_table_pages: >10d}" 4691 4692 snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries 4693 idx = 0 4694 while idx < count: 4695 current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *')) 4696 print(entry_format.format(index=idx, e=current_entry)) 4697 idx +=1 4698 4699 ShowJetsamZprintSnapshot() 4700 return 4701 4702# EndMacro: showjetsamsnapshot 4703 4704# Macro: showjetsambucket 4705@lldb_command('showjetsamband', 'J') 4706def ShowJetsamBand(cmd_args=[], cmd_options={}): 4707 """ Print the processes in a jetsam band. 4708 Usage: showjetsamband band_number [-J] 4709 -J : Output pids as json 4710 """ 4711 if cmd_args is None or len(cmd_args) != 1: 4712 raise ArgumentError("invalid arguments") 4713 4714 print_json = "-J" in cmd_options 4715 4716 bucket_number = int(cmd_args[0]) 4717 buckets = kern.GetGlobalVariable('memstat_bucket') 4718 bucket = value(buckets.GetSBValue().CreateValueFromExpression(None, 4719 'memstat_bucket[%d]' %(bucket_number))) 4720 l = bucket.list 4721 4722 pids = [] 4723 if not print_json: 4724 print(GetProcSummary.header) 4725 for i in IterateTAILQ_HEAD(l, "p_memstat_list"): 4726 pids.append(int(i.p_pid)) 4727 if not print_json: 4728 print(GetProcSummary(i)) 4729 4730 as_json = json.dumps(pids) 4731 if print_json: 4732 print(as_json) 4733 4734# Macro: showvnodecleanblk/showvnodedirtyblk 4735 4736def _GetBufSummary(buf): 4737 """ Get a summary of important information out of a buf_t. 4738 """ 4739 initial = "(struct buf) {0: <#0x} =" 4740 4741 # List all of the fields in this buf summary. 4742 entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq, 4743 buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid, 4744 buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp, 4745 buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata, 4746 buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff, 4747 buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr] 4748 4749 # Join an (already decent) string representation of each field 4750 # with newlines and indent the region. 4751 joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ") 4752 4753 # Add the total string representation to our title and return it. 4754 out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n" 4755 return out_str 4756 4757def _ShowVnodeBlocks(dirty=True, cmd_args=None): 4758 """ Display info about all [dirty|clean] blocks in a vnode. 4759 """ 4760 if cmd_args is None or len(cmd_args) == 0: 4761 print("Please provide a valid vnode argument.") 4762 return 4763 4764 vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *') 4765 list_head = vnodeval.v_cleanblkhd; 4766 if dirty: 4767 list_head = vnodeval.v_dirtyblkhd 4768 4769 print("Blocklist for vnode {}:".format(cmd_args[0])) 4770 4771 i = 0 4772 for buf in IterateListEntry(list_head, 'b_hash'): 4773 # For each block (buf_t) in the appropriate list, 4774 # ask for a summary and print it. 4775 print("---->\nblock {}: ".format(i) + _GetBufSummary(buf)) 4776 i += 1 4777 return 4778 4779@lldb_command('showvnodecleanblk') 4780def ShowVnodeCleanBlocks(cmd_args=None): 4781 """ Display info about all clean blocks in a vnode. 4782 usage: showvnodecleanblk <address of vnode> 4783 """ 4784 _ShowVnodeBlocks(False, cmd_args) 4785 4786@lldb_command('showvnodedirtyblk') 4787def ShowVnodeDirtyBlocks(cmd_args=None): 4788 """ Display info about all dirty blocks in a vnode. 4789 usage: showvnodedirtyblk <address of vnode> 4790 """ 4791 _ShowVnodeBlocks(True, cmd_args) 4792 4793# EndMacro: showvnodecleanblk/showvnodedirtyblk 4794 4795 4796@lldb_command("vm_page_lookup_in_map") 4797def VmPageLookupInMap(cmd_args=None): 4798 """Lookup up a page at a virtual address in a VM map 4799 usage: vm_page_lookup_in_map <map> <vaddr> 4800 """ 4801 if cmd_args is None or len(cmd_args) < 2: 4802 raise ArgumentError() 4803 4804 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 4805 vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t') 4806 print("vaddr {:#018x} in map {: <#018x}".format(vaddr, map)) 4807 vm_page_lookup_in_map(map, vaddr) 4808 4809def vm_page_lookup_in_map(map, vaddr): 4810 vaddr = unsigned(vaddr) 4811 vme_list_head = map.hdr.links 4812 vme_ptr_type = GetType('vm_map_entry *') 4813 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"): 4814 if unsigned(vme.links.start) > vaddr: 4815 break 4816 if unsigned(vme.links.end) <= vaddr: 4817 continue 4818 offset_in_vme = vaddr - unsigned(vme.links.start) 4819 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))) 4820 offset_in_object = offset_in_vme + get_vme_offset(vme) 4821 obj_or_submap = get_vme_object(vme) 4822 if vme.is_sub_map: 4823 print("vaddr {:#018x} in map {: <#018x}".format(offset_in_object, obj_or_submap)) 4824 return vm_page_lookup_in_map(obj_or_submap, offset_in_object) 4825 else: 4826 return vm_page_lookup_in_object(obj_or_submap, offset_in_object) 4827 4828@lldb_command("vm_page_lookup_in_object") 4829def VmPageLookupInObject(cmd_args=None): 4830 """Lookup up a page at a given offset in a VM object 4831 usage: vm_page_lookup_in_object <object> <offset> 4832 """ 4833 if cmd_args is None or len(cmd_args) < 2: 4834 raise ArgumentError() 4835 4836 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t') 4837 offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t') 4838 print("offset {:#018x} in object {: <#018x}".format(offset, object)) 4839 vm_page_lookup_in_object(object, offset) 4840 4841def vm_page_lookup_in_object(object, offset): 4842 offset = unsigned(offset) 4843 page_size = kern.globals.page_size 4844 trunc_offset = offset & ~(page_size - 1) 4845 print(" offset {:#018x} in VM object {: <#018x}".format(offset, object)) 4846 hash_id = _calc_vm_page_hash(object, trunc_offset) 4847 page_list = kern.globals.vm_page_buckets[hash_id].page_list 4848 page = _vm_page_unpack_ptr(page_list) 4849 while page != 0: 4850 m = kern.GetValueFromAddress(page, 'vm_page_t') 4851 m_object_val = _vm_page_unpack_ptr(m.vmp_object) 4852 m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t') 4853 if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset): 4854 page = _vm_page_unpack_ptr(m.vmp_next_m) 4855 continue 4856 print(" resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))) 4857 return m 4858 if object.pager and object.pager_ready: 4859 offset_in_pager = trunc_offset + unsigned(object.paging_offset) 4860 if not object.internal: 4861 print(" offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)) 4862 return None 4863 pager = Cast(object.pager, 'compressor_pager *') 4864 ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager) 4865 if ret: 4866 return None 4867 if object.shadow and not object.phys_contiguous: 4868 offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset) 4869 vm_page_lookup_in_object(object.shadow, offset_in_shadow) 4870 return None 4871 print(" page is absent and will be zero-filled on demand") 4872 return None 4873 4874@lldb_command("vm_page_lookup_in_compressor_pager") 4875def VmPageLookupInCompressorPager(cmd_args=None): 4876 """Lookup up a page at a given offset in a compressor pager 4877 usage: vm_page_lookup_in_compressor_pager <pager> <offset> 4878 """ 4879 if cmd_args is None or len(cmd_args) < 2: 4880 raise ArgumentError() 4881 4882 pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t') 4883 offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t') 4884 print("offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)) 4885 vm_page_lookup_in_compressor_pager(pager, offset) 4886 4887def vm_page_lookup_in_compressor_pager(pager, offset): 4888 offset = unsigned(offset) 4889 page_size = unsigned(kern.globals.page_size) 4890 page_num = unsigned(offset // page_size) 4891 if page_num > pager.cpgr_num_slots: 4892 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)) 4893 return 0 4894 slots_per_chunk = 512 // sizeof ('compressor_slot_t') 4895 num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) // slots_per_chunk) 4896 if num_chunks > 1: 4897 chunk_idx = unsigned(page_num // slots_per_chunk) 4898 chunk = pager.cpgr_slots.cpgr_islots[chunk_idx] 4899 slot_idx = unsigned(page_num % slots_per_chunk) 4900 slot = GetObjectAtIndexFromArray(chunk, slot_idx) 4901 slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx) 4902 elif pager.cpgr_num_slots > 2: 4903 slot_idx = page_num 4904 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx) 4905 slot_str = "dslots[{:d}]".format(slot_idx) 4906 else: 4907 slot_idx = page_num 4908 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx) 4909 slot_str = "eslots[{:d}]".format(slot_idx) 4910 print(" offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)) 4911 if slot == 0: 4912 return 0 4913 slot_value = dereference(slot) 4914 print(" value {:#010x}".format(slot_value)) 4915 vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t')) 4916 return 1 4917 4918@lldb_command("vm_page_lookup_in_compressor") 4919def VmPageLookupInCompressor(cmd_args=None): 4920 """Lookup up a page in a given compressor slot 4921 usage: vm_page_lookup_in_compressor <slot> 4922 """ 4923 if cmd_args is None or len(cmd_args) == 0: 4924 raise ArgumentError() 4925 4926 slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *') 4927 print("compressor slot {: <#018x}".format(slot)) 4928 vm_page_lookup_in_compressor(slot) 4929 4930C_SV_CSEG_ID = ((1 << 22) - 1) 4931 4932def vm_page_lookup_in_compressor(slot_ptr): 4933 slot_ptr = Cast(slot_ptr, 'compressor_slot_t *') 4934 slot_value = dereference(slot_ptr) 4935 slot = Cast(slot_value, 'c_slot_mapping') 4936 print(slot) 4937 print("compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)) 4938 if slot_ptr == 0: 4939 return 4940 if slot.s_cseg == C_SV_CSEG_ID: 4941 sv = kern.globals.c_segment_sv_hash_table 4942 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)) 4943 return 4944 if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available): 4945 print("*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))) 4946 return 4947 c_segments = kern.globals.c_segments 4948 c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1) 4949 c_seg = c_segments_elt.c_seg 4950 c_no_data = 0 4951 if hasattr(c_seg, 'c_state'): 4952 c_state = c_seg.c_state 4953 if c_state == 0: 4954 c_state_str = "C_IS_EMPTY" 4955 c_no_data = 1 4956 elif c_state == 1: 4957 c_state_str = "C_IS_FREE" 4958 c_no_data = 1 4959 elif c_state == 2: 4960 c_state_str = "C_IS_FILLING" 4961 elif c_state == 3: 4962 c_state_str = "C_ON_AGE_Q" 4963 elif c_state == 4: 4964 c_state_str = "C_ON_SWAPOUT_Q" 4965 elif c_state == 5: 4966 c_state_str = "C_ON_SWAPPEDOUT_Q" 4967 c_no_data = 1 4968 elif c_state == 6: 4969 c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q" 4970 c_no_data = 1 4971 elif c_state == 7: 4972 c_state_str = "C_ON_SWAPPEDIN_Q" 4973 elif c_state == 8: 4974 c_state_str = "C_ON_MAJORCOMPACT_Q" 4975 elif c_state == 9: 4976 c_state_str = "C_ON_BAD_Q" 4977 c_no_data = 1 4978 else: 4979 c_state_str = "<unknown>" 4980 else: 4981 c_state = -1 4982 c_state_str = "<no c_state field>" 4983 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)) 4984 c_indx = unsigned(slot.s_cindx) 4985 if hasattr(c_seg, 'c_slot_var_array'): 4986 c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len 4987 if c_indx < c_seg_fixed_array_len: 4988 cs = c_seg.c_slot_fixed_array[c_indx] 4989 else: 4990 cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len) 4991 else: 4992 C_SEG_SLOT_ARRAY_SIZE = 64 4993 C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1 4994 cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx // C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK) 4995 print(cs) 4996 kmem = kmemory.KMem.get_shared() 4997 c_slot_unpacked_ptr = kmem.c_slot_packing.unpack(unsigned(cs.c_packed_ptr)) 4998 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))) 4999 if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr): 5000 print("*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)) 5001 if c_no_data == 0: 5002 c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset) 5003 c_size = cs.c_size 5004 cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size) 5005 print(cmd) 5006 print(lldb_run_command(cmd)) 5007 else: 5008 print("<no compressed data>") 5009 5010# From vm_page.h 5011VM_PAGE_NOT_ON_Q = 0 5012VM_PAGE_IS_WIRED = 1 5013VM_PAGE_USED_BY_COMPRESSOR = 2 5014VM_PAGE_ON_FREE_Q = 3 5015VM_PAGE_ON_FREE_LOCAL_Q = 4 5016VM_PAGE_ON_FREE_LOPAGE_Q = 5 5017VM_PAGE_ON_THROTTLED_Q = 6 5018VM_PAGE_ON_PAGEOUT_Q = 7 5019VM_PAGE_ON_SPECULATIVE_Q = 8 5020VM_PAGE_ON_ACTIVE_LOCAL_Q = 9 5021VM_PAGE_ON_ACTIVE_Q = 10 5022VM_PAGE_ON_INACTIVE_INTERNAL_Q = 11 5023VM_PAGE_ON_INACTIVE_EXTERNAL_Q = 12 5024VM_PAGE_ON_INACTIVE_CLEANED_Q = 13 5025VM_PAGE_ON_SECLUDED_Q = 14 5026 5027@lldb_command('vm_scan_all_pages') 5028def VMScanAllPages(cmd_args=None): 5029 """Scans the vm_pages[] array 5030 """ 5031 vm_pages_count = kern.globals.vm_pages_count 5032 vm_pages = kern.globals.vm_pages 5033 5034 free_count = 0 5035 local_free_count = 0 5036 active_count = 0 5037 local_active_count = 0 5038 inactive_count = 0 5039 speculative_count = 0 5040 throttled_count = 0 5041 wired_count = 0 5042 compressor_count = 0 5043 pageable_internal_count = 0 5044 pageable_external_count = 0 5045 secluded_count = 0 5046 secluded_free_count = 0 5047 secluded_inuse_count = 0 5048 5049 i = 0 5050 for i in range(vm_pages_count): 5051 if i % 10000 == 0: 5052 print("{:d}/{:d}...\n".format(i,vm_pages_count)) 5053 5054 m = vm_pages[i] 5055 internal = False 5056 5057 m_object_addr = _vm_page_unpack_ptr(m.vmp_object) 5058 if m_object_addr != 0 and (m_object := kern.CreateValueFromAddress(m_object_addr, "struct vm_object")).GetSBValue().IsValid() and m_object.internal: 5059 internal = True 5060 5061 m_vmp_q_state = int(m.vmp_q_state) 5062 if m.vmp_wire_count != 0 and m_vmp_q_state != VM_PAGE_ON_ACTIVE_LOCAL_Q: 5063 wired_count = wired_count + 1 5064 pageable = 0 5065 elif m_vmp_q_state == VM_PAGE_ON_THROTTLED_Q: 5066 throttled_count = throttled_count + 1 5067 pageable = 0 5068 elif m_vmp_q_state == VM_PAGE_ON_ACTIVE_Q: 5069 active_count = active_count + 1 5070 pageable = 1 5071 elif m_vmp_q_state == VM_PAGE_ON_ACTIVE_LOCAL_Q: 5072 local_active_count = local_active_count + 1 5073 pageable = 0 5074 elif m_vmp_q_state in (VM_PAGE_ON_INACTIVE_CLEANED_Q, VM_PAGE_ON_INACTIVE_INTERNAL_Q, 5075 VM_PAGE_ON_INACTIVE_EXTERNAL_Q): 5076 inactive_count = inactive_count + 1 5077 pageable = 1 5078 elif m_vmp_q_state == VM_PAGE_ON_SPECULATIVE_Q: 5079 speculative_count = speculative_count + 1 5080 pageable = 0 5081 elif m_vmp_q_state == VM_PAGE_ON_FREE_Q: 5082 free_count = free_count + 1 5083 pageable = 0 5084 elif m_vmp_q_state == VM_PAGE_ON_SECLUDED_Q: 5085 secluded_count = secluded_count + 1 5086 if m_object_addr == 0: 5087 secluded_free_count = secluded_free_count + 1 5088 else: 5089 secluded_inuse_count = secluded_inuse_count + 1 5090 pageable = 0 5091 elif m_object_addr == 0 and m.vmp_busy: 5092 local_free_count = local_free_count + 1 5093 pageable = 0 5094 elif m_vmp_q_state == VM_PAGE_USED_BY_COMPRESSOR: 5095 compressor_count = compressor_count + 1 5096 pageable = 0 5097 else: 5098 print("weird page vm_pages[{:d}]?\n".format(i)) 5099 pageable = 0 5100 5101 if pageable: 5102 if internal: 5103 pageable_internal_count = pageable_internal_count + 1 5104 else: 5105 pageable_external_count = pageable_external_count + 1 5106 5107 print("vm_pages_count = {:d}\n".format(vm_pages_count)) 5108 5109 print("wired_count = {:d}\n".format(wired_count)) 5110 print("throttled_count = {:d}\n".format(throttled_count)) 5111 print("active_count = {:d}\n".format(active_count)) 5112 print("local_active_count = {:d}\n".format(local_active_count)) 5113 print("inactive_count = {:d}\n".format(inactive_count)) 5114 print("speculative_count = {:d}\n".format(speculative_count)) 5115 print("free_count = {:d}\n".format(free_count)) 5116 print("local_free_count = {:d}\n".format(local_free_count)) 5117 print("compressor_count = {:d}\n".format(compressor_count)) 5118 5119 print("pageable_internal_count = {:d}\n".format(pageable_internal_count)) 5120 print("pageable_external_count = {:d}\n".format(pageable_external_count)) 5121 print("secluded_count = {:d}\n".format(secluded_count)) 5122 print("secluded_free_count = {:d}\n".format(secluded_free_count)) 5123 print("secluded_inuse_count = {:d}\n".format(secluded_inuse_count)) 5124 5125 5126@lldb_command('show_all_vm_named_entries') 5127def ShowAllVMNamedEntries(cmd_args=None): 5128 """ Routine to print a summary listing of all the VM named entries 5129 """ 5130 5131 kmem = kmemory.KMem.get_shared() 5132 ikot_named_entry = GetEnumValue('ipc_kotype_t', 'IKOT_NAMED_ENTRY') 5133 5134 port_ty = gettype('struct ipc_port') 5135 ent_ty = gettype('struct vm_named_entry') 5136 5137 named_entries = ( 5138 port 5139 for port 5140 in kmemory.Zone("ipc ports").iter_allocated(port_ty) 5141 if port.xGetScalarByPath(".ip_object.io_bits") & 0x3ff == ikot_named_entry 5142 ) 5143 5144 for idx, port in enumerate(named_entries): 5145 ko = kmem.make_address(port.xGetScalarByName('ip_kobject')) 5146 ent = port.xCreateValueFromAddress(None, ko, ent_ty) 5147 showmemoryentry(value(ent.AddressOf()), idx=idx + 1, port=value(port.AddressOf())) 5148 5149@lldb_command('show_vm_named_entry') 5150def ShowVMNamedEntry(cmd_args=None): 5151 """ Routine to print a VM named entry 5152 """ 5153 if cmd_args is None or len(cmd_args) == 0: 5154 raise ArgumentError() 5155 5156 named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t') 5157 showmemoryentry(named_entry) 5158 5159def showmemoryentry(entry, idx=0, port=None): 5160 """ Routine to print out a summary a VM memory entry 5161 params: 5162 entry - core.value : a object of type 'struct vm_named_entry *' 5163 returns: 5164 None 5165 """ 5166 show_pager_info = True 5167 show_all_shadows = True 5168 5169 backing = "" 5170 if entry.is_sub_map == 1: 5171 backing += "SUBMAP" 5172 if entry.is_copy == 1: 5173 backing += "COPY" 5174 if entry.is_object == 1: 5175 backing += "OBJECT" 5176 if entry.is_sub_map == 0 and entry.is_copy == 0 and entry.is_object == 0: 5177 backing += "***?***" 5178 prot="" 5179 if entry.protection & 0x1: 5180 prot += "r" 5181 else: 5182 prot += "-" 5183 if entry.protection & 0x2: 5184 prot += "w" 5185 else: 5186 prot += "-" 5187 if entry.protection & 0x4: 5188 prot += "x" 5189 else: 5190 prot += "-" 5191 extra_str = "" 5192 if port is not None: 5193 extra_str += " port={:#016x}".format(port) 5194 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)) 5195 5196 if entry.is_sub_map == 1: 5197 showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows) 5198 elif entry.is_copy == 1: 5199 showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows) 5200 elif entry.is_object == 1: 5201 showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows) 5202 else: 5203 print("***** UNKNOWN TYPE *****") 5204 print() 5205 5206@lldb_command("showmaprb") 5207def ShowMapRB(cmd_args=None): 5208 """Routine to print out a VM map's RB tree 5209 usage: showmaprb <vm_map> 5210 """ 5211 if cmd_args is None or len(cmd_args) == 0: 5212 raise ArgumentError() 5213 5214 5215 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t') 5216 print(GetVMMapSummary.header) 5217 print(GetVMMapSummary(map_val)) 5218 5219 vme_type = gettype('struct vm_map_entry') 5220 to_entry = vme_type.xContainerOfTransform('store') 5221 5222 print(GetVMEntrySummary.header) 5223 for links in iter_RB_HEAD(map_val.hdr.rb_head_store.GetSBValue(), 'entry'): 5224 print(GetVMEntrySummary(value(to_entry(links).AddressOf()))) 5225 return None 5226 5227@lldb_command('show_all_owned_objects', 'T') 5228def ShowAllOwnedObjects(cmd_args=None, cmd_options={}): 5229 """ Routine to print the list of VM objects owned by each task 5230 -T: show only ledger-tagged objects 5231 """ 5232 showonlytagged = False 5233 if "-T" in cmd_options: 5234 showonlytagged = True 5235 for task in kern.tasks: 5236 ShowTaskOwnedVmObjects(task, showonlytagged) 5237 5238@lldb_command('show_task_owned_objects', 'T') 5239def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}): 5240 """ Routine to print the list of VM objects owned by the specified task 5241 -T: show only ledger-tagged objects 5242 """ 5243 if cmd_args is None or len(cmd_args) == 0: 5244 raise ArgumentError() 5245 5246 showonlytagged = False 5247 if "-T" in cmd_options: 5248 showonlytagged = True 5249 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 5250 ShowTaskOwnedVmObjects(task, showonlytagged) 5251 5252@lldb_command('showdeviceinfo', 'J') 5253def ShowDeviceInfo(cmd_args=None, cmd_options={}): 5254 """ Routine to show basic device information (model, build, ncpus, etc...) 5255 Usage: memstats [-J] 5256 -J : Output json 5257 """ 5258 print_json = False 5259 if "-J" in cmd_options: 5260 print_json = True 5261 device_info = {} 5262 device_info["build"] = str(kern.globals.osversion) 5263 device_info["memoryConfig"] = int(kern.globals.max_mem_actual) 5264 device_info["ncpu"] = int(kern.globals.ncpu) 5265 device_info["pagesize"] = int(kern.globals.page_size) 5266 device_info["mlockLimit"] = signed(kern.globals.vm_global_user_wire_limit) 5267 # Serializing to json here ensure we always catch bugs preventing 5268 # serialization 5269 as_json = json.dumps(device_info) 5270 5271 5272 if print_json: 5273 print(as_json) 5274 else: 5275 PrettyPrintDictionary(device_info) 5276 5277def ShowTaskOwnedVmObjects(task, showonlytagged=False): 5278 """ Routine to print out a summary listing of all the entries in a vm_map 5279 params: 5280 task - core.value : a object of type 'task *' 5281 returns: 5282 None 5283 """ 5284 taskobjq_total = lambda:None 5285 taskobjq_total.objects = 0 5286 taskobjq_total.vsize = 0 5287 taskobjq_total.rsize = 0 5288 taskobjq_total.wsize = 0 5289 taskobjq_total.csize = 0 5290 vmo_list_head = task.task_objq 5291 vmo_ptr_type = GetType('vm_object *') 5292 idx = 0 5293 for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"): 5294 idx += 1 5295 if not showonlytagged or vmo.vo_ledger_tag != 0: 5296 if taskobjq_total.objects == 0: 5297 print(' \n') 5298 print(GetTaskSummary.header + ' ' + GetProcSummary.header) 5299 print(GetTaskSummary(task) + ' ' + GetProcSummary(GetProcFromTask(task))) 5300 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")) 5301 ShowOwnedVmObject(vmo, idx, 0, taskobjq_total) 5302 if taskobjq_total.objects != 0: 5303 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)) 5304 return None 5305 5306def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total): 5307 """ Routine to print out a VM object owned by a task 5308 params: 5309 object - core.value : a object of type 'struct vm_object *' 5310 returns: 5311 None 5312 """ 5313 page_size = kern.globals.page_size 5314 if object.purgable == 0: 5315 purgable = "N" 5316 elif object.purgable == 1: 5317 purgable = "V" 5318 elif object.purgable == 2: 5319 purgable = "E" 5320 elif object.purgable == 3: 5321 purgable = "D" 5322 else: 5323 purgable = "?" 5324 if object.pager == 0: 5325 compressed_count = 0 5326 else: 5327 compressor_pager = Cast(object.pager, 'compressor_pager *') 5328 compressed_count = compressor_pager.cpgr_num_slots_occupied 5329 5330 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))) 5331 5332 taskobjq_total.objects += 1 5333 taskobjq_total.vsize += object.vo_un1.vou_size // page_size 5334 taskobjq_total.rsize += object.resident_page_count 5335 taskobjq_total.wsize += object.wired_page_count 5336 taskobjq_total.csize += compressed_count 5337 5338def GetProcPIDForObjectOwner(owner): 5339 """ same as GetProcPIDForTask() but deals with -1 for a disowned object 5340 """ 5341 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)): 5342 return -1 5343 return GetProcPIDForTask(owner) 5344 5345def GetProcNameForObjectOwner(owner): 5346 """ same as GetProcNameForTask() but deals with -1 for a disowned object 5347 """ 5348 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)): 5349 return "<disowned>" 5350 return GetProcNameForTask(owner) 5351 5352def GetDescForNamedEntry(mem_entry): 5353 out_str = "\n" 5354 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) 5355 if mem_entry.is_sub_map: 5356 out_str += " is_sub_map" 5357 elif mem_entry.is_copy: 5358 out_str += " is_copy" 5359 elif mem_entry.is_object: 5360 out_str += " is_object" 5361 else: 5362 out_str += " ???" 5363 return out_str 5364 5365# Macro: showdiagmemthresholds 5366def GetDiagThresholdConvertSizeToString(size,human_readable): 5367 if human_readable == 1 : 5368 if(size > (1 << 20)) : 5369 return "{0: >7,.2f}MB".format(size / (1 << 20)) 5370 elif(size > (1 << 10)) : 5371 return "{0: >7,.2f}KB".format(size / (1 << 10)) 5372 return "{0: >7,.2f}B".format(float(size)) 5373 else : 5374 return "{0: >9d}B".format(size ) 5375 5376@header("{: >8s} {: >14s} {: >14s} {: >10s} {: >14s} {: >10s} {: >10s} {: <32s}".format( 5377'PID', 'Footprint', 5378'Limit', 'Lim Warned','Threshold', 'Thr Warned','Thr Enabled','Command')) 5379def GetDiagThresholdStatusNode(proc_val,interested_pid,show_all,human_readable): 5380 """ Internal function to get memorystatus information from the given proc 5381 params: proc - value representing struct proc * 5382 return: str - formatted output information for proc object 5383 5384 Options are 5385 -p Define a pid to show information 5386 -a Print all the processes, regardless if threshold is enabled 5387 -r Show data in human readable format 5388 """ 5389 5390 if interested_pid != -1 and int(interested_pid) != int(GetProcPID(proc_val)) : 5391 return "" 5392 5393 5394 LF_ENTRY_ACTIVE = 0x0001 # entry is active if set 5395 LF_WAKE_NEEDED = 0x0100 # one or more threads are asleep 5396 LF_WAKE_INPROGRESS = 0x0200 # the wait queue is being processed 5397 LF_REFILL_SCHEDULED = 0x0400 # a refill timer has been set 5398 LF_REFILL_INPROGRESS = 0x0800 # the ledger is being refilled 5399 LF_CALLED_BACK = 0x1000 # callback was called for balance in deficit 5400 LF_WARNED = 0x2000 # callback was called for balance warning 5401 LF_TRACKING_MAX = 0x4000 # track max balance. Exclusive w.r.t refill 5402 LF_PANIC_ON_NEGATIVE = 0x8000 # panic if it goes negative 5403 LF_TRACK_CREDIT_ONLY = 0x10000 # only update "credit" 5404 LF_DIAG_WARNED = 0x20000 # callback was called for balance diag 5405 LF_DIAG_DISABLED = 0x40000 # diagnostics threshold are disabled at the moment 5406 5407 out_str = '' 5408 task_val = GetTaskFromProc(proc_val) 5409 if task_val is None: 5410 return out_str 5411 5412 task_ledgerp = task_val.ledger 5413 ledger_template = kern.globals.task_ledger_template 5414 5415 task_phys_footprint_ledger_entry = GetLedgerEntryWithName(ledger_template, task_ledgerp, 'phys_footprint') 5416 5417 diagmem_threshold = task_phys_footprint_ledger_entry['diag_threshold_scaled'] 5418 if diagmem_threshold == -1 and show_all == 0 and interested_pid == -1 : 5419 return "" 5420 5421 diagmem_threshold_warned = task_phys_footprint_ledger_entry['flags'] & LF_DIAG_WARNED 5422 diagmem_threshold_disabled = task_phys_footprint_ledger_entry['flags'] & LF_DIAG_DISABLED 5423 5424 phys_footprint_limit = task_phys_footprint_ledger_entry['limit'] 5425 phys_footprint_limit_warned = task_phys_footprint_ledger_entry['flags'] & LF_WARNED 5426 task_mem_footprint = task_phys_footprint_ledger_entry['balance'] 5427 5428 5429 if phys_footprint_limit_warned == 0 : 5430 phys_footprint_limit_warned_str = "Not warned" 5431 else : 5432 phys_footprint_limit_warned_str = "Warned" 5433 5434 if diagmem_threshold_warned == 0 : 5435 diagmem_threshold_warned_str = "Not warned" 5436 else : 5437 diagmem_threshold_warned_str = "Warned" 5438 5439 if diagmem_threshold_disabled == 0 : 5440 diagmem_threshold_disabled_str = "Enabled" 5441 else : 5442 diagmem_threshold_disabled_str = "Disabled" 5443 5444 if diagmem_threshold == -1 : 5445 diagmem_threshold_str = "Not set" 5446 else : 5447 diagmem_threshold_str = GetDiagThresholdConvertSizeToString(diagmem_threshold * (1<<20),human_readable) 5448 # PID FP LIM LIMW THR THRW THRD Name 5449 format_string = '{0: >8d} {1: >14s} {2: >14s} {3: >10s} {4: >14s} {5: >10s} {6: >10s} {7: <32s}' 5450 out_str += format_string.format( 5451 GetProcPID(proc_val), 5452 GetDiagThresholdConvertSizeToString(task_mem_footprint,human_readable), 5453 GetDiagThresholdConvertSizeToString(phys_footprint_limit,human_readable), 5454 phys_footprint_limit_warned_str, 5455 diagmem_threshold_str, 5456 diagmem_threshold_warned_str, 5457 diagmem_threshold_disabled_str, 5458 GetProcName(proc_val) 5459 ) 5460 return out_str 5461 5462@lldb_command('showdiagmemthresholds','P:AR') 5463def ShowDiagmemThresholds(cmd_args=None, cmd_options={}): 5464 """ Routine to display each entry in diagmem threshold and its ledger related information 5465 Usage: showdiagmemthresholds 5466 Options are 5467 -P Define a pid to show information 5468 -A Print all the processes, regardless if threshold is enabled 5469 -R Show data in human readable format 5470 """ 5471 # If we are focusing only on one PID, lets check 5472 if "-P" in cmd_options: 5473 interested_pid = cmd_options["-P"] 5474 else : 5475 interested_pid = -1 5476 5477 5478 if "-A" in cmd_options: 5479 show_all = 1 5480 else : 5481 show_all = 0 5482 5483 if "-R" in cmd_options: 5484 human_readable = 1 5485 else : 5486 human_readable = 0 5487 5488 bucket_index = 0 5489 bucket_count = 20 5490 print(GetDiagThresholdStatusNode.header) 5491 while bucket_index < bucket_count: 5492 current_bucket = kern.globals.memstat_bucket[bucket_index] 5493 current_list = current_bucket.list 5494 current_proc = Cast(current_list.tqh_first, 'proc *') 5495 while unsigned(current_proc) != 0: 5496 current_line = GetDiagThresholdStatusNode(current_proc,interested_pid,show_all,human_readable) 5497 if current_line != "" : 5498 print(current_line) 5499 current_proc = current_proc.p_memstat_list.tqe_next 5500 bucket_index += 1 5501 print("\n\n") 5502 5503 # EndMacro: showdiagmemthresholds 5504 5505