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