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