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