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