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.
14 @lldb_command('memstats')
15 def Memstats(cmd_args
=None):
16 """ 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.
19 print "memorystatus_level: {: >10d}".format(kern
.globals.memorystatus_level
)
20 print "memorystatus_available_pages: {: >10d}".format(kern
.globals.memorystatus_available_pages
)
21 print "inuse_ptepages_count: {: >10d}".format(kern
.globals.inuse_ptepages_count
)
24 print "vm_page_throttled_count: {: >10d}".format(kern
.globals.vm_page_throttled_count
)
25 print "vm_page_active_count: {: >10d}".format(kern
.globals.vm_page_active_count
)
26 print "vm_page_inactive_count: {: >10d}".format(kern
.globals.vm_page_inactive_count
)
27 print "vm_page_wire_count: {: >10d}".format(kern
.globals.vm_page_wire_count
)
28 print "vm_page_free_count: {: >10d}".format(kern
.globals.vm_page_free_count
)
29 print "vm_page_purgeable_count: {: >10d}".format(kern
.globals.vm_page_purgeable_count
)
30 print "vm_page_inactive_target: {: >10d}".format(kern
.globals.vm_page_inactive_target
)
31 print "vm_page_free_target: {: >10d}".format(kern
.globals.vm_page_free_target
)
33 print "vm_page_free_reserved: {: >10d}".format(kern
.globals.vm_page_free_reserved
)
35 @xnudebug_test('test_memstats')
36 def TestMemstats(kernel_target
, config
, lldb_obj
, isConnected
):
37 """ Test the functionality of memstats command
43 print "Target is not connected. Cannot test memstats"
45 res
= lldb
.SBCommandReturnObject()
46 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("memstats", res
)
47 result
= res
.GetOutput()
48 if result
.split(":")[1].strip().find('None') == -1 :
55 # Macro: showmemorystatus
56 def CalculateLedgerPeak(phys_footprint_entry
):
57 """ Internal function to calculate ledger peak value for the given phys footprint entry
58 params: phys_footprint_entry - value representing struct ledger_entry *
59 return: value - representing the ledger peak for the given phys footprint entry
61 now
= kern
.globals.sched_tick
/ 20
62 ledger_peak
= long(phys_footprint_entry
.le_credit
) - long(phys_footprint_entry
.le_debit
)
63 if hasattr(phys_footprint_entry
._le
._le
_max
, 'le_interval_max') and (long(phys_footprint_entry
._le
._le
_max
.le_interval_max
) > ledger_peak
):
64 ledger_peak
= long(phys_footprint_entry
._le
._le
_max
.le_interval_max
)
67 @header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s} {: <20s}\n".format(
68 'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint',
69 'recent peak', 'lifemax', 'limit', 'command'))
70 def GetMemoryStatusNode(proc_val
):
71 """ Internal function to get memorystatus information from the given proc
72 params: proc - value representing struct proc *
73 return: str - formatted output information for proc object
76 task_val
= Cast(proc_val
.task
, 'task *')
77 task_ledgerp
= task_val
.ledger
79 task_physmem_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.phys_mem
]
80 task_iokit_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.iokit_mapped
]
81 task_phys_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.phys_footprint
]
82 page_size
= kern
.globals.page_size
84 phys_mem_footprint
= (long(task_physmem_footprint_ledger_entry
.le_credit
) - long(task_physmem_footprint_ledger_entry
.le_debit
)) / page_size
85 iokit_footprint
= (long(task_iokit_footprint_ledger_entry
.le_credit
) - long(task_iokit_footprint_ledger_entry
.le_debit
)) / page_size
86 phys_footprint
= (long(task_phys_footprint_ledger_entry
.le_credit
) - long(task_phys_footprint_ledger_entry
.le_debit
)) / page_size
87 phys_footprint_limit
= long(task_phys_footprint_ledger_entry
.le_limit
) / page_size
88 ledger_peak
= CalculateLedgerPeak(task_phys_footprint_ledger_entry
)
89 phys_footprint_spike
= ledger_peak
/ page_size
90 phys_footprint_lifetime_max
= long(task_phys_footprint_ledger_entry
._le
._le
_max
.le_lifetime_max
) / page_size
92 format_string
= '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}'
93 out_str
+= format_string
.format(proc_val
.p_pid
, proc_val
.p_memstat_effectivepriority
,
94 proc_val
.p_memstat_requestedpriority
, proc_val
.p_memstat_state
, proc_val
.p_memstat_relaunch_flags
,
95 proc_val
.p_memstat_userdata
, phys_mem_footprint
, iokit_footprint
, phys_footprint
)
96 if phys_footprint
!= phys_footprint_spike
:
97 out_str
+= "{: >12d}".format(phys_footprint_spike
)
99 out_str
+= "{: >12s}".format('-')
101 out_str
+= "{: >10d} ".format(phys_footprint_lifetime_max
)
102 out_str
+= "{: >10d} {: <20s}\n".format(phys_footprint_limit
, proc_val
.p_comm
)
105 @lldb_command('showmemorystatus')
106 def ShowMemoryStatus(cmd_args
=None):
107 """ Routine to display each entry in jetsam list with a summary of pressure statistics
108 Usage: showmemorystatus
112 print GetMemoryStatusNode
.header
113 print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)",
114 "(pages)", "(pages)", "(pages)")
115 while bucket_index
< bucket_count
:
116 current_bucket
= kern
.globals.memstat_bucket
[bucket_index
]
117 current_list
= current_bucket
.list
118 current_proc
= Cast(current_list
.tqh_first
, 'proc *')
119 while unsigned(current_proc
) != 0:
120 print GetMemoryStatusNode(current_proc
)
121 current_proc
= current_proc
.p_memstat_list
.tqe_next
126 # EndMacro: showmemorystatus
128 def GetRealMetadata(meta
):
129 """ Get real metadata for a given metadata pointer
132 if unsigned(meta
.zindex
) != 0x03FF:
135 return kern
.GetValueFromAddress(unsigned(meta
) - unsigned(meta
.real_metadata_offset
), "struct zone_page_metadata *")
139 def GetFreeList(meta
):
140 """ Get the free list pointer for a given metadata pointer
143 zone_map_min_address
= kern
.GetGlobalVariable('zone_map_min_address')
144 zone_map_max_address
= kern
.GetGlobalVariable('zone_map_max_address')
146 if unsigned(meta
.freelist_offset
) == unsigned(0xffffffff):
149 if (unsigned(meta
) >= unsigned(zone_map_min_address
)) and (unsigned(meta
) < unsigned(zone_map_max_address
)):
150 page_index
= ((unsigned(meta
) - unsigned(kern
.GetGlobalVariable('zone_metadata_region_min'))) / sizeof('struct zone_page_metadata'))
151 return (unsigned(zone_map_min_address
) + (kern
.globals.page_size
* (page_index
))) + meta
.freelist_offset
153 return (unsigned(meta
) + meta
.freelist_offset
)
157 @lldb_type_summary(['zone_page_metadata'])
158 @header("{:<18s} {:<18s} {:>8s} {:>8s} {:<18s} {:<20s}".format('ZONE_METADATA', 'FREELIST', 'PG_CNT', 'FREE_CNT', 'ZONE', 'NAME'))
159 def GetZoneMetadataSummary(meta
):
160 """ Summarize a zone metadata object
161 params: meta - obj representing zone metadata in the kernel
162 returns: str - summary of the zone metadata
168 out_str
+= 'Metadata Description:\n' + GetZoneMetadataSummary
.header
+ '\n'
169 meta
= kern
.GetValueFromAddress(meta
, "struct zone_page_metadata *")
170 if unsigned(meta
.zindex
) == 255:
171 out_str
+= "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}\n".format(meta
, 0, 0, 0, 0, '(fake multipage meta)')
172 meta
= GetRealMetadata(meta
)
175 zinfo
= kern
.globals.zone_array
[unsigned(meta
.zindex
)]
176 out_str
+= "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}".format(meta
, GetFreeList(meta
), meta
.page_count
, meta
.free_count
, addressof(zinfo
), zinfo
.zone_name
)
182 @header("{:<18s} {:>18s} {:>18s} {:<18s}".format('ADDRESS', 'TYPE', 'OFFSET_IN_PG', 'METADATA'))
184 """ Information about kernel pointer
188 pagesize
= kern
.globals.page_size
189 zone_map_min_address
= kern
.GetGlobalVariable('zone_map_min_address')
190 zone_map_max_address
= kern
.GetGlobalVariable('zone_map_max_address')
191 if (unsigned(addr
) >= unsigned(zone_map_min_address
)) and (unsigned(addr
) < unsigned(zone_map_max_address
)):
192 zone_metadata_region_min
= kern
.GetGlobalVariable('zone_metadata_region_min')
193 zone_metadata_region_max
= kern
.GetGlobalVariable('zone_metadata_region_max')
194 if (unsigned(addr
) >= unsigned(zone_metadata_region_min
)) and (unsigned(addr
) < unsigned(zone_metadata_region_max
)):
195 metadata_offset
= (unsigned(addr
) - unsigned(zone_metadata_region_min
)) % sizeof('struct zone_page_metadata')
196 page_offset_str
= "{:d}/{:d}".format((unsigned(addr
) - (unsigned(addr
) & ~
(pagesize
- 1))), pagesize
)
197 out_str
+= WhatIs
.header
+ '\n'
198 out_str
+= "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr
), "Metadata", page_offset_str
, unsigned(addr
) - metadata_offset
)
199 out_str
+= GetZoneMetadataSummary((unsigned(addr
) - metadata_offset
)) + '\n\n'
201 page_index
= ((unsigned(addr
) & ~
(pagesize
- 1)) - unsigned(zone_map_min_address
)) / pagesize
202 meta
= unsigned(zone_metadata_region_min
) + (page_index
* sizeof('struct zone_page_metadata'))
203 meta
= kern
.GetValueFromAddress(meta
, "struct zone_page_metadata *")
204 page_meta
= GetRealMetadata(meta
)
206 zinfo
= kern
.globals.zone_array
[unsigned(page_meta
.zindex
)]
207 page_offset_str
= "{:d}/{:d}".format((unsigned(addr
) - (unsigned(addr
) & ~
(pagesize
- 1))), pagesize
)
208 out_str
+= WhatIs
.header
+ '\n'
209 out_str
+= "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr
), "Element", page_offset_str
, page_meta
)
210 out_str
+= GetZoneMetadataSummary(unsigned(page_meta
)) + '\n\n'
212 out_str
+= "Unmapped address within the zone_map ({:#018x}-{:#018x})".format(zone_map_min_address
, zone_map_max_address
)
214 out_str
+= "Address {:#018x} is outside the zone_map ({:#018x}-{:#018x})\n".format(addr
, zone_map_min_address
, zone_map_max_address
)
218 @lldb_command('whatis')
219 def WhatIsHelper(cmd_args
=None):
220 """ Routine to show information about a kernel pointer
221 Usage: whatis <address>
224 raise ArgumentError("No arguments passed")
225 addr
= kern
.GetValueFromAddress(cmd_args
[0], 'void *')
229 data_array
= kern
.GetValueFromAddress(unsigned(addr
) - 16, "uint8_t *")
230 print_hex_data(data_array
[0:48], unsigned(addr
) - 16, "")
237 @lldb_type_summary(['zone','zone_t'])
238 @header("{:^18s} {:<40s} {:>10s} {:>10s} {:>10s} {:>10s}".format(
239 'ZONE', 'NAME', 'CACHE_ELTS', 'DEP_VALID', 'DEP_EMPTY','DEP_FULL'))
241 def GetZoneCacheSummary(zone
):
242 """ Summarize a zone's cache with important information.
244 zone: value - obj representing a zone in kernel
246 str - summary of the zone's cache contents
249 format_string
= '{:#018x} {:<40s} {:>10d} {:>10s} {:>10d} {:>10d}'
251 mag_capacity
= kern
.GetGlobalVariable('magazine_element_count')
252 depot_capacity
= kern
.GetGlobalVariable('depot_element_count')
255 if zone
.__getattr
__('cpu_cache_enabled') :
256 for i
in range(0, kern
.globals.machine_info
.physical_cpu
):
257 cache
= zone
.zcache
[0].zcc_per_cpu_caches
[i
]
258 cache_elem_count
+= cache
.current
.zcc_magazine_index
259 cache_elem_count
+= cache
.previous
.zcc_magazine_index
261 if zone
.zcache
[0].zcc_depot_index
!= -1:
262 cache_elem_count
+= zone
.zcache
[0].zcc_depot_index
* mag_capacity
263 out_string
+= format_string
.format(zone
, zone
.zone_name
, cache_elem_count
, "Y", depot_capacity
- zone
.zcache
[0].zcc_depot_index
, zone
.zcache
[0].zcc_depot_index
)
265 out_string
+= format_string
.format(zone
, zone
.zone_name
, cache_elem_count
, "N", 0, 0)
269 @lldb_command('showzcache')
270 def ZcachePrint(cmd_args
=None):
271 """ Routine to print a summary listing of all the kernel zones cache contents
272 All columns are printed in decimal
275 print GetZoneCacheSummary
.header
276 for zval
in kern
.zones
:
277 if zval
.__getattr
__('cpu_cache_enabled') :
278 print GetZoneCacheSummary(zval
)
280 # EndMacro: showzcache
282 # Macro: showzcachecpu
284 @lldb_type_summary(['zone','zone_t'])
285 @header("{:^18s} {:40s} {:>10s} {:>10s}".format(
286 'ZONE', 'NAME', 'CACHE_ELTS', 'CPU_INFO'))
288 def GetZoneCacheCPUSummary(zone
):
289 """ Summarize a zone's cache broken up per cpu
291 zone: value - obj representing a zone in kernel
293 str - summary of the zone's per CPU cache contents
296 format_string
= '{:#018x} {:40s} {:10d} {cpuinfo:s}'
300 mag_capacity
= kern
.GetGlobalVariable('magazine_element_count')
301 depot_capacity
= kern
.GetGlobalVariable('depot_element_count')
304 if zone
.__getattr
__('cpu_cache_enabled') :
305 for i
in range(0, kern
.globals.machine_info
.physical_cpu
):
308 cache
= zone
.zcache
[0].zcc_per_cpu_caches
[i
]
309 per_cpu_count
= cache
.current
.zcc_magazine_index
310 per_cpu_count
+= cache
.previous
.zcc_magazine_index
311 cache_elem_count
+= per_cpu_count
312 cpu_info
+= "CPU {:d}: {:5}".format(i
,per_cpu_count
)
313 if zone
.zcache
[0].zcc_depot_index
!= -1:
314 cache_elem_count
+= zone
.zcache
[0].zcc_depot_index
* mag_capacity
316 out_string
+= format_string
.format(zone
, zone
.zone_name
, cache_elem_count
,cpuinfo
= cpu_info
)
320 @lldb_command('showzcachecpu')
321 def ZcacheCPUPrint(cmd_args
=None):
322 """ Routine to print a summary listing of all the kernel zones cache contents
323 All columns are printed in decimal
326 print GetZoneCacheCPUSummary
.header
327 for zval
in kern
.zones
:
328 if zval
.__getattr
__('cpu_cache_enabled') :
329 print GetZoneCacheCPUSummary(zval
)
331 # EndMacro: showzcachecpu
335 @lldb_type_summary(['zone','zone_t'])
336 @header(("{:<18s} {:_^23s} {:_^24s} {:_^13s} {:_^31s}\n"+
337 "{:<18s} {:>11s} {:>11s} {:>8s} {:>7s} {:>7s} {:>6s} {:>6s} {:>7s} {:>5s} {:>3s} {:>5s} {:>7s} {:<15s} {:<20s}").format(
338 '', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG',
339 'ZONE', 'ALLOC', 'FREE', 'ALLOC', 'FREE', 'CACHE', 'COUNT', 'FREE', 'SIZE', 'ELTS', 'PGS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
340 def GetZoneSummary(zone
):
341 """ Summarize a zone with important information. See help zprint for description of each field
343 zone: value - obj representing a zone in kernel
345 str - summary of the zone
348 format_string
= '{zone:#018x} {zone.cur_size:11,d} {free_size:11,d} {zone.count:8,d} {zone.countfree:7,d} {cache_elem_count:7,d} {zone.page_count:6,d} {zone.count_all_free_pages:6,d} {zone.alloc_size:7,d} {alloc_count:5,d} {alloc_pages:3,d} {alloc_waste:5,d} {zone.elem_size:7,d} {markings:<15s} {zone.zone_name:<20s} '
349 pagesize
= kern
.globals.page_size
351 free_size
= zone
.countfree
* zone
.elem_size
352 mag_capacity
= kern
.GetGlobalVariable('magazine_element_count')
354 alloc_pages
= zone
.alloc_size
/ pagesize
355 alloc_count
= zone
.alloc_size
/ zone
.elem_size
356 alloc_waste
= zone
.alloc_size
% zone
.elem_size
359 ["collectable", "C"],
362 ["caller_acct", "@"],
363 ["exhaustible", "H"],
364 ["allows_foreign", "F"],
365 ["async_prio_refill", "R"],
368 ["doing_alloc_without_vm_priv", "A"],
369 ["doing_alloc_with_vm_priv", "S"],
371 ["cpu_cache_enabled", "E"]
373 if kern
.arch
== 'x86_64':
374 marks
.append(["gzalloc_exempt", "M"])
375 marks
.append(["alignment_required", "N"])
378 if not zone
.__getattr
__("zone_valid") :
381 if zone
.__getattr
__(mark
[0]) :
386 if zone
.__getattr
__('cpu_cache_enabled') :
387 for i
in range(0, kern
.globals.machine_info
.physical_cpu
):
388 cache
= zone
.zcache
[0].zcc_per_cpu_caches
[i
]
389 cache_elem_count
+= cache
.current
.zcc_magazine_index
390 cache_elem_count
+= cache
.previous
.zcc_magazine_index
391 if zone
.zcache
[0].zcc_depot_index
!= -1:
392 cache_elem_count
+= zone
.zcache
[0].zcc_depot_index
* mag_capacity
394 out_string
+= format_string
.format(zone
=zone
, free_size
=free_size
, alloc_count
=alloc_count
,
395 alloc_pages
=alloc_pages
, alloc_waste
=alloc_waste
, cache_elem_count
=cache_elem_count
, markings
=markings
)
397 if zone
.exhaustible
:
398 out_string
+= "(max: {:d})".format(zone
.max_size
)
402 @lldb_command('zprint', fancy
=True)
403 def Zprint(cmd_args
=None, cmd_options
={}, O
=None):
404 """ Routine to print a summary listing of all the kernel zones
405 All columns are printed in decimal
409 $ - not encrypted during hibernation
410 @ - allocs and frees are accounted to caller process for KPRVT
412 F - allows foreign memory (memory not allocated from zone_map)
413 M - gzalloc will avoid monitoring this zone
414 R - will be refilled when below low water mark
415 O - does not allow refill callout to fill zone on noblock allocation
416 N - zone requires alignment (avoids padding this zone for debugging)
417 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
418 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
419 W - another thread is waiting for more memory
420 E - Per-cpu caching is enabled for this zone
421 L - zone is being monitored by zleaks
422 G - currently running GC
423 I - zone was destroyed and is no longer valid
426 with O
.table(GetZoneSummary
.header
):
427 for zval
in kern
.zones
:
428 print GetZoneSummary(zval
)
430 @xnudebug_test('test_zprint')
431 def TestZprint(kernel_target
, config
, lldb_obj
, isConnected
):
432 """ Test the functionality of zprint command
438 print "Target is not connected. Cannot test memstats"
440 res
= lldb
.SBCommandReturnObject()
441 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("zprint", res
)
442 result
= res
.GetOutput()
443 if len(result
.split("\n")) > 2:
451 # Macro: showzfreelist
453 def ShowZfreeListHeader(zone
):
454 """ Helper routine to print a header for zone freelist.
455 (Since the freelist does not have a custom type, this is not defined as a Type Summary).
457 zone:zone_t - Zone object to print header info
462 scaled_factor
= (unsigned(kern
.globals.zp_factor
) +
463 (unsigned(zone
.elem_size
) >> unsigned(kern
.globals.zp_scale
)))
466 out_str
+= "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
467 out_str
+= "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
468 zone
.elem_size
, zone
.count
, kern
.globals.zp_nopoison_cookie
, kern
.globals.zp_poisoned_cookie
, zone
.zp_count
, scaled_factor
)
469 out_str
+= "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
470 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
473 def ShowZfreeListChain(zone
, zfirst
, zlimit
):
474 """ Helper routine to print a zone free list chain
476 zone: zone_t - Zone object
477 zfirst: void * - A pointer to the first element of the free list chain
478 zlimit: int - Limit for the number of elements to be printed by showzfreelist
482 current
= Cast(zfirst
, 'void *')
483 while ShowZfreeList
.elts_found
< zlimit
:
484 ShowZfreeList
.elts_found
+= 1
485 znext
= dereference(Cast(current
, 'vm_offset_t *'))
486 znext
= (unsigned(znext
) ^
unsigned(kern
.globals.zp_nopoison_cookie
))
487 znext
= kern
.GetValueFromAddress(znext
, 'vm_offset_t *')
488 backup_ptr
= kern
.GetValueFromAddress((unsigned(Cast(current
, 'vm_offset_t')) + unsigned(zone
.elem_size
) - sizeof('vm_offset_t')), 'vm_offset_t *')
489 backup_val
= dereference(backup_ptr
)
490 n_unobfuscated
= (unsigned(backup_val
) ^
unsigned(kern
.globals.zp_nopoison_cookie
))
491 p_unobfuscated
= (unsigned(backup_val
) ^
unsigned(kern
.globals.zp_poisoned_cookie
))
493 if p_unobfuscated
== unsigned(znext
):
494 poison_str
= "P ({0: <d})".format(ShowZfreeList
.elts_found
- ShowZfreeList
.last_poisoned
)
495 ShowZfreeList
.last_poisoned
= ShowZfreeList
.elts_found
497 if n_unobfuscated
!= unsigned(znext
):
498 poison_str
= "INVALID"
499 print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
500 ShowZfreeList
.elts_found
, unsigned(current
), unsigned(znext
), unsigned(backup_val
), n_unobfuscated
, p_unobfuscated
, poison_str
)
501 if unsigned(znext
) == 0:
503 current
= Cast(znext
, 'void *')
505 @static_var('elts_found',0)
506 @static_var('last_poisoned',0)
507 @lldb_command('showzfreelist')
508 def ShowZfreeList(cmd_args
=None):
509 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
510 Usage: showzfreelist <zone> [iterations]
512 Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
515 print ShowZfreeList
.__doc
__
517 ShowZfreeList
.elts_found
= 0
518 ShowZfreeList
.last_poisoned
= 0
520 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
522 if len(cmd_args
) >= 2:
523 zlimit
= ArgumentStringToInt(cmd_args
[1])
524 ShowZfreeListHeader(zone
)
526 if unsigned(zone
.allows_foreign
) == 1:
527 for free_page_meta
in IterateQueue(zone
.pages
.any_free_foreign
, 'struct zone_page_metadata *', 'pages'):
528 if ShowZfreeList
.elts_found
== zlimit
:
530 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
531 if unsigned(zfirst
) != 0:
532 ShowZfreeListChain(zone
, zfirst
, zlimit
)
533 for free_page_meta
in IterateQueue(zone
.pages
.intermediate
, 'struct zone_page_metadata *', 'pages'):
534 if ShowZfreeList
.elts_found
== zlimit
:
536 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
537 if unsigned(zfirst
) != 0:
538 ShowZfreeListChain(zone
, zfirst
, zlimit
)
539 for free_page_meta
in IterateQueue(zone
.pages
.all_free
, 'struct zone_page_metadata *', 'pages'):
540 if ShowZfreeList
.elts_found
== zlimit
:
542 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
543 if unsigned(zfirst
) != 0:
544 ShowZfreeListChain(zone
, zfirst
, zlimit
)
546 if ShowZfreeList
.elts_found
== zlimit
:
547 print "Stopped at {0: <d} elements!".format(zlimit
)
549 print "Found {0: <d} elements!".format(ShowZfreeList
.elts_found
)
551 # EndMacro: showzfreelist
553 # Macro: zstack_showzonesbeinglogged
555 @lldb_command('zstack_showzonesbeinglogged')
556 def ZstackShowZonesBeingLogged(cmd_args
=None):
557 """ Show all zones which have BTLog enabled.
560 for zval
in kern
.zones
:
562 print "Zone: %s with its BTLog at: 0x%lx" % (zval
.zone_name
, zval
.zlog_btlog
)
564 # EndMacro: zstack_showzonesbeinglogged
568 @lldb_command('zstack')
569 def Zstack(cmd_args
=None):
570 """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>.
571 Usage: zstack <btlog addr> <index> [<count>]
573 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
574 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
579 if int(kern
.globals.log_records
) == 0:
580 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
583 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
584 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
585 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
586 btrecords
= unsigned(btlog_ptr
.btrecords
)
587 btlog_size
= unsigned(sizeof('struct btlog'))
588 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
589 zstack_index
= ArgumentStringToInt(cmd_args
[1])
591 if len(cmd_args
) >= 3:
592 count
= ArgumentStringToInt(cmd_args
[2])
594 max_count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
596 if (zstack_index
+ count
) > max_count
:
597 count
= max_count
- zstack_index
599 while count
and (zstack_index
!= 0xffffff):
600 zstack_record_offset
= zstack_index
* btrecord_size
601 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
602 if int(zstack_record
.ref_count
)!=0:
603 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
609 # Macro: zstack_inorder
611 @lldb_command('zstack_inorder')
612 def ZstackInOrder(cmd_args
=None):
613 """ Zone leak debugging: Print the stack traces starting from head to the tail.
614 Usage: zstack_inorder <btlog addr>
617 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
619 if int(kern
.globals.log_records
) == 0:
620 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
623 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
624 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
625 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
626 btrecords
= unsigned(btlog_ptr
.btrecords
)
627 btlog_size
= unsigned(sizeof('struct btlog'))
628 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
629 zstack_head
= unsigned(btlog_ptr
.head
)
630 zstack_index
= zstack_head
631 zstack_tail
= unsigned(btlog_ptr
.tail
)
632 count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
634 while count
and (zstack_index
!= 0xffffff):
635 zstack_record_offset
= zstack_index
* btrecord_size
636 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
637 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
638 zstack_index
= zstack_record
.next
641 # EndMacro : zstack_inorder
645 @lldb_command('findoldest')
646 def FindOldest(cmd_args
=None):
649 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
651 # EndMacro : findoldest
653 # Macro : zstack_findleak
655 @lldb_command('zstack_findleak')
656 def zstack_findleak(cmd_args
=None):
657 """ Zone leak debugging: search the log and print the stack with the most active references
659 Usage: zstack_findleak <btlog address>
661 This is useful for verifying a suspected stack as being the source of
664 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
665 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
666 btrecords
= unsigned(btlog_ptr
.btrecords
)
668 cpcs_index
= unsigned(btlog_ptr
.head
)
669 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
674 while cpcs_index
!= 0xffffff:
675 cpcs_record_offset
= cpcs_index
* btrecord_size
676 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
677 if cpcs_record
.ref_count
> highref
:
678 highref_record
= cpcs_record
679 highref
= cpcs_record
.ref_count
680 highref_index
= cpcs_index
681 cpcs_index
= cpcs_record
.next
682 ShowZStackRecord(highref_record
, highref_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
684 # EndMacro: zstack_findleak
688 @lldb_command('findelem')
689 def FindElem(cmd_args
=None):
692 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
696 @lldb_command('zstack_findelem')
697 def ZStackFindElem(cmd_args
=None):
698 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
699 refer to the given zone element.
700 Usage: zstack_findelem <btlog addr> <elem addr>
702 When the kernel panics due to a corrupted zone element, get the
703 element address and use this command. This will show you the stack traces of all logged zalloc and
704 zfree operations which tells you who touched the element in the recent past. This also makes
705 double-frees readily apparent.
708 print ZStackFindElem
.__doc
__
710 if int(kern
.globals.log_records
) == 0 or unsigned(kern
.globals.corruption_debug_flag
) == 0:
711 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
714 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
715 target_element
= unsigned(kern
.GetValueFromAddress(cmd_args
[1], 'void *'))
717 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
718 btrecords
= unsigned(btlog_ptr
.btrecords
)
719 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
723 hashelem
= cast(btlog_ptr
.elem_linkage_un
.element_hash_queue
.tqh_first
, 'btlog_element_t *')
724 if (target_element
>> 32) != 0:
725 target_element
= target_element ^
0xFFFFFFFFFFFFFFFF
727 target_element
= target_element ^
0xFFFFFFFF
729 if unsigned(hashelem
.elem
) == target_element
:
730 recindex
= hashelem
.recindex
731 recoffset
= recindex
* btrecord_size
732 record
= kern
.GetValueFromAddress(btrecords
+ recoffset
, 'btlog_record_t *')
734 if record
.operation
== 1:
735 out_str
+= "OP: ALLOC. "
737 out_str
+= "OP: FREE. "
738 out_str
+= "Stack Index {0: <d} {1: <s}\n".format(recindex
, ('-' * 8))
740 print GetBtlogBacktrace(depth
, record
)
742 if int(record
.operation
) == prev_op
:
743 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
745 prev_op
= int(record
.operation
)
747 hashelem
= cast(hashelem
.element_hash_link
.tqe_next
, 'btlog_element_t *')
749 if scan_items
% 100 == 0:
750 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items
)
752 # EndMacro: zstack_findelem
754 @lldb_command('zstack_findtop', 'N:')
755 def ShowZstackTop(cmd_args
=None, cmd_options
={}):
756 """ Zone leak debugging: search the log and print the stacks with the most active references
759 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
763 raise ArgumentError('Missing required btlog address argument')
766 if '-N' in cmd_options
:
767 n
= int(cmd_options
['-N'])
769 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
770 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
771 btrecords
= unsigned(btlog_ptr
.btrecords
)
773 cpcs_index
= unsigned(btlog_ptr
.head
)
774 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
777 while cpcs_index
!= 0xffffff:
778 cpcs_record_offset
= cpcs_index
* btrecord_size
779 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
780 cpcs_record
.index
= cpcs_index
781 records
.append(cpcs_record
)
782 cpcs_index
= cpcs_record
.next
784 recs
= sorted(records
, key
=lambda x
: x
.ref_count
, reverse
=True)
787 ShowZStackRecord(rec
, rec
.index
, depth
, unsigned(btlog_ptr
.active_element_count
))
789 # EndMacro: zstack_findtop
793 @lldb_command('btlog_find', "AS")
794 def BtlogFind(cmd_args
=None, cmd_options
={}):
797 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
800 #EndMacro: btlog_find
804 @lldb_command('showzalloc')
805 def ShowZalloc(cmd_args
=None):
806 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
807 Usage: showzalloc <index>
810 print ShowZalloc
.__doc
__
812 if unsigned(kern
.globals.zallocations
) == 0:
813 print "zallocations array not initialized!"
815 zallocation
= kern
.globals.zallocations
[ArgumentStringToInt(cmd_args
[0])]
817 ShowZTrace([str(int(zallocation
.za_trace_index
))])
819 #EndMacro: showzalloc
823 @lldb_command('showztrace')
824 def ShowZTrace(cmd_args
=None):
825 """ Prints the backtrace from the ztraces array at index
826 Usage: showztrace <trace index>
829 print ShowZTrace
.__doc
__
831 if unsigned(kern
.globals.ztraces
) == 0:
832 print "ztraces array not initialized!"
834 ztrace_addr
= kern
.globals.ztraces
[ArgumentStringToInt(cmd_args
[0])]
836 ShowZstackTraceHelper(ztrace_addr
.zt_stack
, ztrace_addr
.zt_depth
)
838 #EndMacro: showztrace
840 #Macro: showztraceaddr
842 @lldb_command('showztraceaddr')
843 def ShowZTraceAddr(cmd_args
=None):
844 """ Prints the struct ztrace passed in.
845 Usage: showztraceaddr <trace address>
848 print ShowZTraceAddr
.__doc
__
850 ztrace_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'struct ztrace *')
851 print dereference(ztrace_ptr
)
852 ShowZstackTraceHelper(ztrace_ptr
.zt_stack
, ztrace_ptr
.zt_depth
)
854 #EndMacro: showztraceaddr
856 #Macro: showzstacktrace
858 @lldb_command('showzstacktrace')
859 def ShowZstackTrace(cmd_args
=None):
860 """ Routine to print a stacktrace stored by OSBacktrace.
861 Usage: showzstacktrace <saved stacktrace> [size]
863 size is optional, defaults to 15.
866 print ShowZstackTrace
.__doc
__
868 void_ptr_type
= gettype('void *')
869 void_double_ptr_type
= void_ptr_type
.GetPointerType()
870 trace
= kern
.GetValueFromAddress(cmd_args
[0], void_double_ptr_type
)
872 if len(cmd_args
) >= 2:
873 trace_size
= ArgumentStringToInt(cmd_args
[1])
874 ShowZstackTraceHelper(trace
, trace_size
)
876 #EndMacro: showzstacktrace
878 def ShowZstackTraceHelper(stack
, depth
):
879 """ Helper routine for printing a zstack.
881 stack: void *[] - An array of pointers representing the Zstack
882 depth: int - The depth of the ztrace stack
887 while trace_current
< depth
:
888 trace_addr
= stack
[trace_current
]
889 symbol_arr
= kern
.SymbolicateFromAddress(unsigned(trace_addr
))
891 symbol_str
= str(symbol_arr
[0].addr
)
894 print '{0: <#x} {1: <s}'.format(trace_addr
, symbol_str
)
897 #Macro: showtopztrace
899 @lldb_command('showtopztrace')
900 def ShowTopZtrace(cmd_args
=None):
901 """ Shows the ztrace with the biggest size.
902 (According to top_ztrace, not by iterating through the hash table)
904 top_trace
= kern
.globals.top_ztrace
905 print 'Index: {0: <d}'.format((unsigned(top_trace
) - unsigned(kern
.globals.ztraces
)) / sizeof('struct ztrace'))
906 print dereference(top_trace
)
907 ShowZstackTraceHelper(top_trace
.zt_stack
, top_trace
.zt_depth
)
909 #EndMacro: showtopztrace
913 @lldb_command('showzallocs')
914 def ShowZallocs(cmd_args
=None):
915 """ Prints all allocations in the zallocations table
917 if unsigned(kern
.globals.zallocations
) == 0:
918 print "zallocations array not initialized!"
920 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
922 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
924 while current_index
< max_zallocation
:
925 current_zalloc
= kern
.globals.zallocations
[current_index
]
926 if int(current_zalloc
.za_element
) != 0:
927 print '{0: <5d} {1: <#018x} {2: <5d} {3: <15d}'.format(current_index
, current_zalloc
.za_element
, current_zalloc
.za_trace_index
, unsigned(current_zalloc
.za_size
))
928 allocation_count
+= 1
930 print 'Total Allocations: {0: <d}'.format(allocation_count
)
932 #EndMacro: showzallocs
934 #Macro: showzallocsfortrace
936 @lldb_command('showzallocsfortrace')
937 def ShowZallocsForTrace(cmd_args
=None):
938 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
939 Usage: showzallocsfortrace <trace index>
942 print ShowZallocsForTrace
.__doc
__
944 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
945 target_index
= ArgumentStringToInt(cmd_args
[0])
947 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
949 while current_index
< max_zallocation
:
950 current_zalloc
= kern
.globals.zallocations
[current_index
]
951 if unsigned(current_zalloc
.za_element
) != 0 and (unsigned(current_zalloc
.za_trace_index
) == unsigned(target_index
)):
952 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index
, current_zalloc
.za_element
, current_zalloc
.za_size
)
953 allocation_count
+= 1
955 print 'Total Allocations: {0: <d}'.format(allocation_count
)
957 #EndMacro: showzallocsfortrace
961 @lldb_command('showztraces')
962 def ShowZTraces(cmd_args
=None):
963 """ Prints all traces with size > 0
965 ShowZTracesAbove([0])
967 #EndMacro: showztraces
969 #Macro: showztracesabove
971 @lldb_command('showztracesabove')
972 def ShowZTracesAbove(cmd_args
=None):
973 """ Prints all traces with size greater than X
974 Usage: showztracesabove <size>
977 print ShowZTracesAbove
.__doc
__
979 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
982 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
983 while current_index
< max_ztrace
:
984 ztrace_current
= kern
.globals.ztraces
[current_index
]
985 if ztrace_current
.zt_size
> unsigned(cmd_args
[0]):
986 print '{0: <5d} {1: <6d}'.format(current_index
, int(ztrace_current
.zt_size
))
989 print 'Total traces: {0: <d}'.format(ztrace_count
)
991 #EndMacro: showztracesabove
993 #Macro: showztracehistogram
995 @lldb_command('showztracehistogram')
996 def ShowZtraceHistogram(cmd_args
=None):
997 """ Prints the histogram of the ztrace table
999 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
1002 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
1003 while current_index
< max_ztrace
:
1004 ztrace_current
= kern
.globals.ztraces
[current_index
]
1005 if ztrace_current
.zt_hit_count
!= 0:
1006 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index
, ztrace_current
.zt_hit_count
, ztrace_current
.zt_collisions
)
1009 print 'Total traces: {0: <d}'.format(ztrace_count
)
1011 #EndMacro: showztracehistogram
1013 #Macro: showzallochistogram
1015 @lldb_command('showzallochistogram')
1016 def ShowZallocHistogram(cmd_args
=None):
1017 """ Prints the histogram for the zalloc table
1019 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
1021 zallocation_count
= 0
1022 max_ztrace
= unsigned(kern
.globals.zleak_alloc_buckets
)
1023 while current_index
< max_ztrace
:
1024 zallocation_current
= kern
.globals.zallocations
[current_index
]
1025 if zallocation_current
.za_hit_count
!= 0:
1026 print '{0: <5d} {1: <9d}'.format(current_index
, zallocation_current
.za_hit_count
)
1027 zallocation_count
+= 1
1029 print 'Total Allocations: {0: <d}'.format(zallocation_count
)
1031 #EndMacro: showzallochistogram
1035 @lldb_command('showzstats')
1036 def ShowZstats(cmd_args
=None):
1037 """ Prints the zone leak detection stats
1039 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern
.globals.z_alloc_collisions
), unsigned(kern
.globals.z_trace_collisions
))
1040 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern
.globals.z_alloc_overwrites
), unsigned(kern
.globals.z_trace_overwrites
))
1041 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern
.globals.z_alloc_recorded
), unsigned(kern
.globals.z_trace_recorded
))
1043 #EndMacro: showzstats
1045 def GetBtlogBacktrace(depth
, zstack_record
):
1046 """ Helper routine for getting a BT Log record backtrace stack.
1048 depth:int - The depth of the zstack record
1049 zstack_record:btlog_record_t * - A BTLog record
1051 str - string with backtrace in it.
1055 if not zstack_record
:
1056 return "Zstack record none!"
1058 depth_val
= unsigned(depth
)
1059 while frame
< depth_val
:
1060 frame_pc
= zstack_record
.bt
[frame
]
1061 if not frame_pc
or int(frame_pc
) == 0:
1063 symbol_arr
= kern
.SymbolicateFromAddress(frame_pc
)
1065 symbol_str
= str(symbol_arr
[0].addr
)
1068 out_str
+= "{0: <#0x} <{1: <s}>\n".format(frame_pc
, symbol_str
)
1072 def ShowZStackRecord(zstack_record
, zstack_index
, btrecord_btdepth
, elements_count
):
1073 """ Helper routine for printing a single zstack record
1075 zstack_record:btlog_record_t * - A BTLog record
1076 zstack_index:int - Index for the record in the BTLog table
1081 if zstack_record
.operation
== 1:
1082 out_str
+= "ALLOC. "
1085 out_str
+= "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index
, zstack_record
.ref_count
, elements_count
, ('-' * 8))
1087 print GetBtlogBacktrace(btrecord_btdepth
, zstack_record
)
1090 # Macro: showioalloc
1092 @lldb_command('showioalloc')
1093 def ShowIOAllocations(cmd_args
=None):
1094 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
1095 Routine to display a summary of memory accounting allocated by IOKit allocators.
1097 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_ivars_size
, (kern
.globals.debug_ivars_size
/ 1024))
1098 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_container_malloc_size
, (kern
.globals.debug_container_malloc_size
/ 1024))
1099 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomalloc_size
, (kern
.globals.debug_iomalloc_size
/ 1024))
1100 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomallocpageable_size
, (kern
.globals.debug_iomallocpageable_size
/ 1024))
1103 # EndMacro: showioalloc
1106 # Macro: showselectmem
1107 @lldb_command('showselectmem', "S:")
1108 def ShowSelectMem(cmd_args
=None, cmd_options
={}):
1109 """ Show memory cached by threads on calls to select.
1111 usage: showselectmem [-v]
1112 -v : print each thread's memory
1113 (one line per thread with non-zero select memory)
1114 -S {addr} : Find the thread whose thread-local select set
1115 matches the given address
1119 if config
['verbosity'] > vHUMAN
:
1121 if "-S" in cmd_options
:
1122 opt_wqs
= unsigned(kern
.GetValueFromAddress(cmd_options
["-S"], 'uint64_t *'))
1124 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options
["-S"]))
1127 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1128 for t
in kern
.tasks
:
1129 for th
in IterateQueue(t
.threads
, 'thread *', 'task_threads'):
1130 uth
= Cast(th
.uthread
, 'uthread *');
1132 if hasattr(uth
, 'uu_allocsize'): # old style
1133 thmem
= uth
.uu_allocsize
1135 elif hasattr(uth
, 'uu_wqstate_sz'): # new style
1136 thmem
= uth
.uu_wqstate_sz
1139 print "What kind of uthread is this?!"
1141 if opt_wqs
and opt_wqs
== unsigned(wqs
):
1142 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs
, unsigned(th
), unsigned(th
.thread_id
))
1143 if verbose
and thmem
> 0:
1144 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t
), unsigned(th
.thread_id
), thmem
)
1147 print "Total: {:d} bytes ({:d} kbytes)".format(selmem
, selmem
/1024)
1148 # Endmacro: showselectmem
1151 # Macro: showtaskvme
1152 @lldb_command('showtaskvme', "PS")
1153 def ShowTaskVmeHelper(cmd_args
=None, cmd_options
={}):
1154 """ Display a summary list of the specified vm_map's entries
1155 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
1156 Use -S flag to show VM object shadow chains
1157 Use -P flag to show pager info (mapped file, compressed pages, ...)
1159 show_pager_info
= False
1160 show_all_shadows
= False
1161 if "-P" in cmd_options
:
1162 show_pager_info
= True
1163 if "-S" in cmd_options
:
1164 show_all_shadows
= True
1165 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1166 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1168 @lldb_command('showallvme', "PS")
1169 def ShowAllVME(cmd_args
=None, cmd_options
={}):
1170 """ Routine to print a summary listing of all the vm map entries
1171 Go Through each task in system and show the vm memory regions
1172 Use -S flag to show VM object shadow chains
1173 Use -P flag to show pager info (mapped file, compressed pages, ...)
1175 show_pager_info
= False
1176 show_all_shadows
= False
1177 if "-P" in cmd_options
:
1178 show_pager_info
= True
1179 if "-S" in cmd_options
:
1180 show_all_shadows
= True
1181 for task
in kern
.tasks
:
1182 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1184 @lldb_command('showallvm')
1185 def ShowAllVM(cmd_args
=None):
1186 """ Routine to print a summary listing of all the vm maps
1188 for task
in kern
.tasks
:
1189 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1190 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1191 print GetVMMapSummary
.header
1192 print GetVMMapSummary(task
.map)
1194 @lldb_command("showtaskvm")
1195 def ShowTaskVM(cmd_args
=None):
1196 """ Display info about the specified task's vm_map
1197 syntax: (lldb) showtaskvm <task_ptr>
1200 print ShowTaskVM
.__doc
__
1202 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1204 print "Unknown arguments."
1206 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1207 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1208 print GetVMMapSummary
.header
1209 print GetVMMapSummary(task
.map)
1212 @lldb_command('showallvmstats')
1213 def ShowAllVMStats(cmd_args
=None):
1214 """ Print a summary of vm statistics in a table format
1216 page_size
= kern
.globals.page_size
1217 vmstats
= lambda:None
1218 vmstats
.wired_count
= 0
1219 vmstats
.resident_count
= 0
1220 vmstats
.resident_max
= 0
1221 vmstats
.internal
= 0
1222 vmstats
.external
= 0
1223 vmstats
.reusable
= 0
1224 vmstats
.compressed
= 0
1225 vmstats
.compressed_peak
= 0
1226 vmstats
.compressed_lifetime
= 0
1229 hdr_format
= "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}"
1230 print hdr_format
.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '')
1231 print hdr_format
.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '')
1232 entry_format
= "{m.hdr.nentries: >6d} {s.wired_count: >10d} {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.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {p.p_pid: >10d} {p.p_comm: <20s} {s.error}"
1234 for task
in kern
.tasks
:
1235 proc
= Cast(task
.bsd_info
, 'proc *')
1236 vmmap
= Cast(task
.map, '_vm_map *')
1238 vmstats
.wired_count
= vmmap
.pmap
.stats
.wired_count
;
1239 vmstats
.resident_count
= unsigned(vmmap
.pmap
.stats
.resident_count
);
1240 vmstats
.resident_max
= vmmap
.pmap
.stats
.resident_max
;
1241 vmstats
.internal
= unsigned(vmmap
.pmap
.stats
.internal
);
1242 vmstats
.external
= unsigned(vmmap
.pmap
.stats
.external
);
1243 vmstats
.reusable
= unsigned(vmmap
.pmap
.stats
.reusable
);
1244 vmstats
.compressed
= unsigned(vmmap
.pmap
.stats
.compressed
);
1245 vmstats
.compressed_peak
= unsigned(vmmap
.pmap
.stats
.compressed_peak
);
1246 vmstats
.compressed_lifetime
= unsigned(vmmap
.pmap
.stats
.compressed_lifetime
);
1247 vmstats
.new_resident_count
= vmstats
.internal
+ vmstats
.external
1249 if vmstats
.internal
< 0:
1250 vmstats
.error
+= '*'
1251 if vmstats
.external
< 0:
1252 vmstats
.error
+= '*'
1253 if vmstats
.reusable
< 0:
1254 vmstats
.error
+= '*'
1255 if vmstats
.compressed
< 0:
1256 vmstats
.error
+= '*'
1257 if vmstats
.compressed_peak
< 0:
1258 vmstats
.error
+= '*'
1259 if vmstats
.compressed_lifetime
< 0:
1260 vmstats
.error
+= '*'
1261 if vmstats
.new_resident_count
+vmstats
.reusable
!= vmstats
.resident_count
:
1262 vmstats
.error
+= '*'
1264 print entry_format
.format(p
=proc
, m
=vmmap
, vsize
=(unsigned(vmmap
.size
) / page_size
), t
=task
, s
=vmstats
)
1267 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
1268 """ Routine to print out a summary listing of all the entries in a vm_map
1270 task - core.value : a object of type 'task *'
1274 print "vm_map entries for task " + hex(task
)
1275 print GetTaskSummary
.header
1276 print GetTaskSummary(task
)
1278 print "Task {0: <#020x} has map = 0x0"
1280 print GetVMMapSummary
.header
1281 print GetVMMapSummary(task
.map)
1282 vme_list_head
= task
.map.hdr
.links
1283 vme_ptr_type
= GetType('vm_map_entry *')
1284 print GetVMEntrySummary
.header
1285 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1286 print GetVMEntrySummary(vme
, show_pager_info
, show_all_shadows
)
1289 @lldb_command("showmap")
1290 def ShowMap(cmd_args
=None):
1291 """ Routine to print out info about the specified vm_map
1292 usage: showmap <vm_map>
1294 if cmd_args
== None or len(cmd_args
) < 1:
1295 print "Invalid argument.", ShowMap
.__doc
__
1297 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1298 print GetVMMapSummary
.header
1299 print GetVMMapSummary(map_val
)
1301 @lldb_command("showmapvme")
1302 def ShowMapVME(cmd_args
=None):
1303 """Routine to print out info about the specified vm_map and its vm entries
1304 usage: showmapvme <vm_map>
1306 if cmd_args
== None or len(cmd_args
) < 1:
1307 print "Invalid argument.", ShowMapVME
.__doc
__
1309 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1310 print GetVMMapSummary
.header
1311 print GetVMMapSummary(map_val
)
1312 vme_list_head
= map_val
.hdr
.links
1313 vme_ptr_type
= GetType('vm_map_entry *')
1314 print GetVMEntrySummary
.header
1315 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1316 print GetVMEntrySummary(vme
)
1319 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
1320 @header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free"))
1321 def GetVMMapSummary(vmmap
):
1322 """ Display interesting bits from vm_map struct """
1324 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
1325 vm_size
= uint64_t(vmmap
.size
).value
1327 if vmmap
.pmap
!= 0: resident_pages
= int(vmmap
.pmap
.stats
.resident_count
)
1329 if int(vmmap
.holelistenabled
) == 0: first_free
= vmmap
.f_s
._first
_free
1330 out_string
+= format_string
.format(vmmap
, vmmap
.pmap
, vm_size
, vmmap
.hdr
.nentries
, resident_pages
, vmmap
.hint
, first_free
)
1333 @lldb_type_summary(['vm_map_entry'])
1334 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1335 def GetVMEntrySummary(vme
):
1336 """ Display vm entry specific information. """
1337 page_size
= kern
.globals.page_size
1339 format_string
= "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1340 vme_protection
= int(vme
.protection
)
1341 vme_max_protection
= int(vme
.max_protection
)
1342 vme_extra_info_str
="SC-Ds"[int(vme
.inheritance
)]
1343 if int(vme
.is_sub_map
) != 0 :
1344 vme_extra_info_str
+="s"
1345 elif int(vme
.needs_copy
) != 0 :
1346 vme_extra_info_str
+="n"
1347 num_pages
= (unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)) / page_size
1348 out_string
+= format_string
.format(vme
, vme
.links
.start
, vme_protection
, vme_max_protection
, vme_extra_info_str
, num_pages
, vme
.vme_object
.vmo_object
, vme
.vme_offset
)
1351 # EndMacro: showtaskvme
1352 @lldb_command('showmapwired')
1353 def ShowMapWired(cmd_args
=None):
1354 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1356 if cmd_args
== None or len(cmd_args
) < 1:
1357 print "Invalid argument", ShowMapWired
.__doc
__
1359 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1362 @lldb_type_summary(['kmod_info_t *'])
1363 @header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name'))
1364 def GetKextSummary(kmod
):
1365 """ returns a string representation of kext information
1368 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1369 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod
.address
), unsigned(kmod
.size
))
1370 text_segment
= macho
.get_text_segment(segments
)
1371 if not text_segment
:
1372 text_segment
= segments
[0]
1373 out_string
+= format_string
.format(kmod
, kmod
.address
, kmod
.size
, kmod
.id, kmod
.reference_count
, text_segment
.vmaddr
, text_segment
.vmsize
, kmod
.version
, kmod
.name
)
1376 @lldb_type_summary(['uuid_t'])
1378 def GetUUIDSummary(uuid
):
1379 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1381 arr
= Cast(addressof(uuid
), 'uint8_t *')
1384 data
.append(int(arr
[i
]))
1385 return "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a
=data
)
1387 @lldb_command('showallkmods')
1388 def ShowAllKexts(cmd_args
=None):
1389 """Display a summary listing of all loaded kexts (alias: showallkmods)
1391 kmod_val
= kern
.globals.kmod
1392 kextuuidinfo
= GetKextLoadInformation(show_progress
=(config
['verbosity'] > vHUMAN
))
1393 print "{: <36s} ".format("UUID") + GetKextSummary
.header
1394 for kval
in IterateLinkedList(kmod_val
, 'next'):
1395 uuid
= "........-....-....-....-............"
1396 kaddr
= unsigned(kval
.address
)
1397 found_kext_summary
= None
1398 for l
in kextuuidinfo
:
1399 if kaddr
== int(l
[3],16):
1401 found_kext_summary
= l
1403 if found_kext_summary
:
1404 _ksummary
= GetKextSummary(found_kext_summary
[7])
1406 _ksummary
= GetKextSummary(kval
)
1407 print uuid
+ " " + _ksummary
1409 def GetKmodWithAddr(addr
):
1410 """ Go through kmod list and find one with begin_addr as addr
1411 returns: None if not found. else a cvalue of type kmod
1413 kmod_val
= kern
.globals.kmod
1414 for kval
in IterateLinkedList(kmod_val
, 'next'):
1415 if addr
== unsigned(kval
.address
):
1419 def GetAllSegmentsAndSectionsFromDataInMemory(address
, size
):
1420 """ reads memory at address and parses mach_header to get segment and section information
1421 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
1422 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
1423 if TEXT segment is not found a dummy segment & section with address, size is returned.
1425 cache_hash
= "kern.kexts.segments.{}.{}".format(address
, size
)
1426 cached_result
= caching
.GetDynamicCacheData(cache_hash
,())
1428 return cached_result
1430 defval
= macho
.MachOSegment('__TEXT', address
, size
, 0, size
)
1431 if address
== 0 or size
== 0:
1432 return ([defval
], [defval
])
1434 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
1435 # until we have separate version. we will pay penalty only on arm64 devices
1436 if not kern
.arch
.startswith('arm64'):
1437 return ([defval
], [defval
])
1439 restrict_size_to_read
= 1536
1441 while machoObject
is None:
1442 err
= lldb
.SBError()
1443 size_to_read
= min(size
, restrict_size_to_read
)
1444 data
= LazyTarget
.GetProcess().ReadMemory(address
, size_to_read
, err
)
1445 if not err
.Success():
1446 print "Failed to read memory at {} and size {}".format(address
, size_to_read
)
1447 return ([defval
], [defval
])
1449 m
= macho
.MemMacho(data
, len(data
))
1451 except Exception as e
:
1452 if str(e
.message
).find('unpack requires a string argument') >= 0:
1453 # this may be due to short read of memory. Lets do double read size.
1454 restrict_size_to_read
*= 2
1455 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read
))
1458 print "Failed to read MachO for address {} errormessage: {}".format(address
, e
.message
)
1459 return ([defval
], [defval
])
1460 # end of while loop. We have machoObject defined
1461 segments
= machoObject
.get_segments_with_name('')
1462 sections
= machoObject
.get_sections_with_name('')
1463 rval
= (segments
, sections
)
1464 caching
.SaveDynamicCacheData(cache_hash
, rval
)
1467 def GetKextLoadInformation(addr
=0, show_progress
=False):
1468 """ Extract the kext uuid and load address information from the kernel data structure.
1470 addr - int - optional integer that is the address to search for.
1472 [] - array with each entry of format
1473 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
1474 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
1476 cached_result
= caching
.GetDynamicCacheData("kern.kexts.loadinformation", [])
1477 ## if specific addr is provided then ignore caching
1478 if cached_result
and not addr
:
1479 return cached_result
1481 # because of <rdar://problem/12683084>, we can't find summaries directly
1482 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1483 baseaddr
= unsigned(kern
.globals.gLoadedKextSummaries
) + 0x10
1484 summaries_begin
= kern
.GetValueFromAddress(baseaddr
, 'OSKextLoadedKextSummary *')
1485 total_summaries
= int(kern
.globals.gLoadedKextSummaries
.numSummaries
)
1486 kext_version
= int(kern
.globals.gLoadedKextSummaries
.version
)
1487 entry_size
= 64 + 16 + 8 + 8 + 8 + 4 + 4
1488 if kext_version
>= 2 :
1489 entry_size
= int(kern
.globals.gLoadedKextSummaries
.entry_size
)
1491 for i
in range(total_summaries
):
1493 print "progress: {}/{}".format(i
, total_summaries
)
1494 tmpaddress
= unsigned(summaries_begin
) + (i
* entry_size
)
1495 current_kext
= kern
.GetValueFromAddress(tmpaddress
, 'OSKextLoadedKextSummary *')
1496 # code to extract macho information
1497 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext
.address
), unsigned(current_kext
.size
))
1498 seginfo
= macho
.get_text_segment(segments
)
1500 seginfo
= segments
[0]
1501 kmod_obj
= GetKmodWithAddr(unsigned(current_kext
.address
))
1503 if addr
== unsigned(current_kext
.address
) or addr
== seginfo
.vmaddr
:
1504 return [(GetUUIDSummary(current_kext
.uuid
) , hex(seginfo
.vmaddr
).rstrip('L'), str(current_kext
.name
), hex(current_kext
.address
), segments
, seginfo
, current_kext
, kmod_obj
)]
1505 retval
.append((GetUUIDSummary(current_kext
.uuid
) , hex(seginfo
.vmaddr
).rstrip('L'), str(current_kext
.name
), hex(current_kext
.address
), segments
, seginfo
, current_kext
, kmod_obj
))
1508 caching
.SaveDynamicCacheData("kern.kexts.loadinformation", retval
)
1511 lldb_alias('showallkexts', 'showallkmods')
1513 def GetOSKextVersion(version_num
):
1514 """ returns a string of format 1.2.3x from the version_num
1515 params: version_num - int
1518 if version_num
== -1 :
1520 (MAJ_MULT
, MIN_MULT
, REV_MULT
,STAGE_MULT
) = (100000000, 1000000, 10000, 1000)
1521 version
= version_num
1523 vers_major
= version
/ MAJ_MULT
1524 version
= version
- (vers_major
* MAJ_MULT
)
1526 vers_minor
= version
/ MIN_MULT
1527 version
= version
- (vers_minor
* MIN_MULT
)
1529 vers_revision
= version
/ REV_MULT
1530 version
= version
- (vers_revision
* REV_MULT
)
1532 vers_stage
= version
/ STAGE_MULT
1533 version
= version
- (vers_stage
* STAGE_MULT
)
1535 vers_stage_level
= version
1537 out_str
= "%d.%d" % (vers_major
, vers_minor
)
1538 if vers_revision
> 0: out_str
+= ".%d" % vers_revision
1539 if vers_stage
== 1 : out_str
+= "d%d" % vers_stage_level
1540 if vers_stage
== 3 : out_str
+= "a%d" % vers_stage_level
1541 if vers_stage
== 5 : out_str
+= "b%d" % vers_stage_level
1542 if vers_stage
== 6 : out_str
+= "fc%d" % vers_stage_level
1546 @lldb_command('showallknownkmods')
1547 def ShowAllKnownKexts(cmd_args
=None):
1548 """ Display a summary listing of all kexts known in the system.
1549 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1551 kext_count
= int(kern
.globals.sKextsByID
.count
)
1553 kext_dictionary
= kern
.globals.sKextsByID
.dictionary
1554 print "%d kexts in sKextsByID:" % kext_count
1555 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1556 format_string
= "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1558 while index
< kext_count
:
1559 kext_dict
= GetObjectAtIndexFromArray(kext_dictionary
, index
)
1560 kext_name
= str(kext_dict
.key
.string
)
1561 osk
= Cast(kext_dict
.value
, 'OSKext *')
1562 if int(osk
.flags
.loaded
) :
1563 load_addr
= "{0: <#020x}".format(osk
.kmod_info
)
1564 id = "{0: >5d}".format(osk
.loadTag
)
1566 load_addr
= "------"
1568 version_num
= unsigned(osk
.version
)
1569 version
= GetOSKextVersion(version_num
)
1570 print format_string
.format(osk
, load_addr
, id, version
, kext_name
)
1575 def FindKmodNameForAddr(addr
):
1576 """ Given an address, return the name of the kext containing that address
1578 addr
= unsigned(addr
)
1579 all_kexts_info
= GetKextLoadInformation()
1580 for kinfo
in all_kexts_info
:
1581 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1583 return kinfo
[7].name
1587 @lldb_command('addkextaddr')
1588 def AddKextAddr(cmd_args
=[]):
1589 """ Given an address, load the kext which contains that address
1590 Syntax: (lldb) addkextaddr <addr>
1592 if len(cmd_args
) < 1:
1593 raise ArgumentError("Insufficient arguments")
1595 addr
= ArgumentStringToInt(cmd_args
[0])
1596 all_kexts_info
= GetKextLoadInformation()
1597 kernel_uuid
= str(kern
.globals.kernel_uuid_string
).lower()
1599 found_segment
= None
1600 for kinfo
in all_kexts_info
:
1601 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1603 print GetKextSummary
.header
1604 print GetKextSummary(kinfo
[7]) + " segment: {} offset = {:#0x}".format(segment
.name
, (addr
- segment
.vmaddr
))
1605 cur_uuid
= kinfo
[0].lower()
1606 if (kernel_uuid
== cur_uuid
):
1609 print "Fetching dSYM for %s" % cur_uuid
1610 info
= dsymForUUID(cur_uuid
)
1611 if info
and 'DBGSymbolRichExecutable' in info
:
1612 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
1613 addDSYM(cur_uuid
, info
)
1614 loadDSYM(cur_uuid
, int(kinfo
[1],16), kinfo
[4])
1616 print "Failed to get symbol info for %s" % cur_uuid
1620 @lldb_command('showkmodaddr')
1621 def ShowKmodAddr(cmd_args
=[]):
1622 """ Given an address, print the offset and name for the kmod containing it
1623 Syntax: (lldb) showkmodaddr <addr>
1625 if len(cmd_args
) < 1:
1626 raise ArgumentError("Insufficient arguments")
1628 addr
= ArgumentStringToInt(cmd_args
[0])
1629 all_kexts_info
= GetKextLoadInformation()
1631 found_segment
= None
1632 for kinfo
in all_kexts_info
:
1633 s
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1639 print GetKextSummary
.header
1640 print GetKextSummary(found_kinfo
[7]) + " segment: {} offset = {:#0x}".format(found_segment
.name
, (addr
- found_segment
.vmaddr
))
1645 @lldb_command('addkext','AF:N:')
1646 def AddKextSyms(cmd_args
=[], cmd_options
={}):
1647 """ Add kext symbols into lldb.
1648 This command finds symbols for a uuid and load the required executable
1650 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1651 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1652 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
1653 addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
1654 addkext all : Will load all the kext symbols - SLOW
1658 if "-F" in cmd_options
:
1659 exec_path
= cmd_options
["-F"]
1660 exec_full_path
= ResolveFSPath(exec_path
)
1661 if not os
.path
.exists(exec_full_path
):
1662 raise ArgumentError("Unable to resolve {:s}".format(exec_path
))
1664 if not os
.path
.isfile(exec_full_path
):
1665 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1666 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1667 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path
))
1672 slide_value
= cmd_args
[0]
1673 debuglog("loading slide value from user input %s" % cmd_args
[0])
1675 filespec
= lldb
.SBFileSpec(exec_full_path
, False)
1676 print "target modules add %s" % exec_full_path
1677 print lldb_run_command("target modules add %s" % exec_full_path
)
1678 loaded_module
= LazyTarget
.GetTarget().FindModule(filespec
)
1679 if loaded_module
.IsValid():
1680 uuid_str
= loaded_module
.GetUUIDString()
1681 debuglog("added module %s with uuid %s" % (exec_full_path
, uuid_str
))
1682 if slide_value
is None:
1683 all_kexts_info
= GetKextLoadInformation()
1684 for k
in all_kexts_info
:
1686 if k
[0].lower() == uuid_str
.lower():
1689 debuglog("found the slide %s for uuid %s" % (k
[1], k
[0]))
1690 if slide_value
is None:
1691 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path
)
1694 cmd_str
= "target modules load --file %s --slide %s" % ( exec_full_path
, str(slide_value
))
1697 cmd_str
= "target modules load --file {} ".format(exec_full_path
)
1700 sections_str
+= " {} {:#0x} ".format(s
.name
, s
.vmaddr
)
1701 cmd_str
+= sections_str
1704 lldb
.debugger
.HandleCommand(cmd_str
)
1706 kern
.symbolicator
= None
1709 all_kexts_info
= GetKextLoadInformation()
1710 kernel_uuid
= str(kern
.globals.kernel_uuid_string
).lower()
1712 if "-N" in cmd_options
:
1713 kext_name
= cmd_options
["-N"]
1714 kext_name_matches
= GetLongestMatchOption(kext_name
, [str(x
[2]) for x
in all_kexts_info
], True)
1715 if len(kext_name_matches
) != 1 and "-A" not in cmd_options
:
1716 print "Ambiguous match for name: {:s}".format(kext_name
)
1717 if len(kext_name_matches
) > 0:
1718 print "Options are:\n\t" + "\n\t".join(kext_name_matches
)
1720 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches
[0], kext_name
))
1721 for cur_knm
in kext_name_matches
:
1722 for x
in all_kexts_info
:
1724 cur_uuid
= x
[0].lower()
1725 if (kernel_uuid
== cur_uuid
):
1728 print "Fetching dSYM for {:s}".format(cur_uuid
)
1729 info
= dsymForUUID(cur_uuid
)
1730 if info
and 'DBGSymbolRichExecutable' in info
:
1731 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid
, info
['DBGSymbolRichExecutable'])
1732 addDSYM(cur_uuid
, info
)
1733 loadDSYM(cur_uuid
, int(x
[1],16), x
[4])
1735 print "Failed to get symbol info for {:s}".format(cur_uuid
)
1737 kern
.symbolicator
= None
1740 if len(cmd_args
) < 1:
1741 raise ArgumentError("No arguments specified.")
1743 uuid
= cmd_args
[0].lower()
1745 load_all_kexts
= False
1747 load_all_kexts
= True
1749 if not load_all_kexts
and len(uuid_regex
.findall(uuid
)) == 0:
1750 raise ArgumentError("Unknown argument {:s}".format(uuid
))
1752 for k_info
in all_kexts_info
:
1753 cur_uuid
= k_info
[0].lower()
1754 if load_all_kexts
or (uuid
== cur_uuid
):
1755 if (kernel_uuid
!= cur_uuid
):
1756 print "Fetching dSYM for %s" % cur_uuid
1757 info
= dsymForUUID(cur_uuid
)
1758 if info
and 'DBGSymbolRichExecutable' in info
:
1759 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
1760 addDSYM(cur_uuid
, info
)
1761 loadDSYM(cur_uuid
, int(k_info
[1],16), k_info
[4])
1763 print "Failed to get symbol info for %s" % cur_uuid
1765 kern
.symbolicator
= None
1770 lldb_alias('showkmod', 'showkmodaddr')
1771 lldb_alias('showkext', 'showkmodaddr')
1772 lldb_alias('showkextaddr', 'showkmodaddr')
1774 @lldb_type_summary(['mount *'])
1775 @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'))
1776 def GetMountSummary(mount
):
1777 """ Display a summary of mount on the system
1779 out_string
= ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1780 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
1781 "{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'))
1784 @lldb_command('showallmounts')
1785 def ShowAllMounts(cmd_args
=None):
1786 """ Print all mount points
1788 mntlist
= kern
.globals.mountlist
1789 print GetMountSummary
.header
1790 for mnt
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
1791 print GetMountSummary(mnt
)
1794 lldb_alias('ShowAllVols', 'showallmounts')
1796 @lldb_command('systemlog')
1797 def ShowSystemLog(cmd_args
=None):
1798 """ Display the kernel's printf ring buffer """
1799 msgbufp
= kern
.globals.msgbufp
1800 msg_size
= int(msgbufp
.msg_size
)
1801 msg_bufx
= int(msgbufp
.msg_bufx
)
1802 msg_bufr
= int(msgbufp
.msg_bufr
)
1803 msg_bufc
= msgbufp
.msg_bufc
1804 msg_bufc_data
= msg_bufc
.GetSBValue().GetPointeeData(0, msg_size
)
1806 # the buffer is circular; start at the write pointer to end,
1807 # then from beginning to write pointer
1809 err
= lldb
.SBError()
1810 for i
in range(msg_bufx
, msg_size
) + range(0, msg_bufx
) :
1812 cbyte
= msg_bufc_data
.GetUnsignedInt8(err
, i
)
1813 if not err
.Success() :
1814 raise ValueError("Failed to read character at offset " + str(i
) + ": " + err
.GetCString())
1829 @static_var('output','')
1830 def _GetVnodePathName(vnode
, vnodename
):
1831 """ Internal function to get vnode path string from vnode structure.
1835 returns Nothing. The output will be stored in the static variable.
1839 if int(vnode
.v_flag
) & 0x1 and int(hex(vnode
.v_mount
), 16) !=0:
1840 if int(vnode
.v_mount
.mnt_vnodecovered
):
1841 _GetVnodePathName(vnode
.v_mount
.mnt_vnodecovered
, str(vnode
.v_mount
.mnt_vnodecovered
.v_name
) )
1843 _GetVnodePathName(vnode
.v_parent
, str(vnode
.v_parent
.v_name
))
1844 _GetVnodePathName
.output
+= "/%s" % vnodename
1846 def GetVnodePath(vnode
):
1847 """ Get string representation of the vnode
1848 params: vnodeval - value representing vnode * in the kernel
1849 return: str - of format /path/to/something
1853 if (int(vnode
.v_flag
) & 0x000001) and int(hex(vnode
.v_mount
), 16) != 0 and (int(vnode
.v_mount
.mnt_flag
) & 0x00004000) :
1856 _GetVnodePathName
.output
= ''
1857 if abs(vnode
.v_name
) != 0:
1858 _GetVnodePathName(vnode
, str(vnode
.v_name
))
1859 out_str
+= _GetVnodePathName
.output
1861 out_str
+= 'v_name = NULL'
1862 _GetVnodePathName
.output
= ''
1866 @lldb_command('showvnodepath')
1867 def ShowVnodePath(cmd_args
=None):
1868 """ Prints the path for a vnode
1869 usage: showvnodepath <vnode>
1871 if cmd_args
!= None and len(cmd_args
) > 0 :
1872 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
1874 print GetVnodePath(vnode_val
)
1877 # Macro: showvnodedev
1878 def GetVnodeDevInfo(vnode
):
1879 """ Internal function to get information from the device type vnodes
1880 params: vnode - value representing struct vnode *
1881 return: str - formatted output information for block and char vnode types passed as param
1883 vnodedev_output
= ""
1884 vblk_type
= GetEnumValue('vtype::VBLK')
1885 vchr_type
= GetEnumValue('vtype::VCHR')
1886 if (vnode
.v_type
== vblk_type
) or (vnode
.v_type
== vchr_type
):
1887 devnode
= Cast(vnode
.v_data
, 'devnode_t *')
1888 devnode_dev
= devnode
.dn_typeinfo
.dev
1889 devnode_major
= (devnode_dev
>> 24) & 0xff
1890 devnode_minor
= devnode_dev
& 0x00ffffff
1892 # boilerplate device information for a vnode
1893 vnodedev_output
+= "Device Info:\n\t vnode:\t\t{:#x}".format(vnode
)
1894 vnodedev_output
+= "\n\t type:\t\t"
1895 if (vnode
.v_type
== vblk_type
):
1896 vnodedev_output
+= "VBLK"
1897 if (vnode
.v_type
== vchr_type
):
1898 vnodedev_output
+= "VCHR"
1899 vnodedev_output
+= "\n\t name:\t\t{:<s}".format(vnode
.v_name
)
1900 vnodedev_output
+= "\n\t major, minor:\t{:d},{:d}".format(devnode_major
, devnode_minor
)
1901 vnodedev_output
+= "\n\t mode\t\t0{:o}".format(unsigned(devnode
.dn_mode
))
1902 vnodedev_output
+= "\n\t owner (u,g):\t{:d} {:d}".format(devnode
.dn_uid
, devnode
.dn_gid
)
1904 # decode device specific data
1905 vnodedev_output
+= "\nDevice Specific Information:\t"
1906 if (vnode
.v_type
== vblk_type
):
1907 vnodedev_output
+= "Sorry, I do not know how to decode block devices yet!"
1908 vnodedev_output
+= "\nMaybe you can write me!"
1910 if (vnode
.v_type
== vchr_type
):
1911 # Device information; this is scanty
1913 if (devnode_major
> 42) or (devnode_major
< 0):
1914 vnodedev_output
+= "Invalid major #\n"
1915 # static assignments in conf
1916 elif (devnode_major
== 0):
1917 vnodedev_output
+= "Console mux device\n"
1918 elif (devnode_major
== 2):
1919 vnodedev_output
+= "Current tty alias\n"
1920 elif (devnode_major
== 3):
1921 vnodedev_output
+= "NULL device\n"
1922 elif (devnode_major
== 4):
1923 vnodedev_output
+= "Old pty slave\n"
1924 elif (devnode_major
== 5):
1925 vnodedev_output
+= "Old pty master\n"
1926 elif (devnode_major
== 6):
1927 vnodedev_output
+= "Kernel log\n"
1928 elif (devnode_major
== 12):
1929 vnodedev_output
+= "Memory devices\n"
1930 # Statically linked dynamic assignments
1931 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptmx_open')):
1932 vnodedev_output
+= "Cloning pty master not done\n"
1933 #GetVnodeDevCpty(devnode_major, devnode_minor)
1934 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptsd_open')):
1935 vnodedev_output
+= "Cloning pty slave not done\n"
1936 #GetVnodeDevCpty(devnode_major, devnode_minor)
1938 vnodedev_output
+= "RESERVED SLOT\n"
1940 vnodedev_output
+= "{:#x} is not a device".format(vnode
)
1941 return vnodedev_output
1943 @lldb_command('showvnodedev')
1944 def ShowVnodeDev(cmd_args
=None):
1945 """ Routine to display details of all vnodes of block and character device types
1946 Usage: showvnodedev <address of vnode>
1949 print "No arguments passed"
1950 print ShowVnodeDev
.__doc
__
1952 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
1954 print "unknown arguments:", str(cmd_args
)
1956 print GetVnodeDevInfo(vnode_val
)
1958 # EndMacro: showvnodedev
1960 # Macro: showvnodelocks
1961 def GetVnodeLock(lockf
):
1962 """ Internal function to get information from the given advisory lock
1963 params: lockf - value representing v_lockf member in struct vnode *
1964 return: str - formatted output information for the advisory lock
1966 vnode_lock_output
= ''
1967 lockf_flags
= lockf
.lf_flags
1968 lockf_type
= lockf
.lf_type
1969 if lockf_flags
& 0x20:
1970 vnode_lock_output
+= ("{: <8s}").format('flock')
1971 if lockf_flags
& 0x40:
1972 vnode_lock_output
+= ("{: <8s}").format('posix')
1973 if lockf_flags
& 0x80:
1974 vnode_lock_output
+= ("{: <8s}").format('prov')
1975 if lockf_flags
& 0x10:
1976 vnode_lock_output
+= ("{: <4s}").format('W')
1977 if lockf_flags
& 0x400:
1978 vnode_lock_output
+= ("{: <8s}").format('ofd')
1980 vnode_lock_output
+= ("{: <4s}").format('.')
1982 # POSIX file vs advisory range locks
1983 if lockf_flags
& 0x40:
1984 lockf_proc
= Cast(lockf
.lf_id
, 'proc *')
1985 vnode_lock_output
+= ("PID {: <18d}").format(lockf_proc
.p_pid
)
1987 vnode_lock_output
+= ("ID {: <#019x}").format(int(lockf
.lf_id
))
1991 vnode_lock_output
+= ("{: <12s}").format('shared')
1994 vnode_lock_output
+= ("{: <12s}").format('exclusive')
1997 vnode_lock_output
+= ("{: <12s}").format('unlock')
1999 vnode_lock_output
+= ("{: <12s}").format('unknown')
2001 # start and stop values
2002 vnode_lock_output
+= ("{: #018x} ..").format(lockf
.lf_start
)
2003 vnode_lock_output
+= ("{: #018x}\n").format(lockf
.lf_end
)
2004 return vnode_lock_output
2006 @header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
2007 def GetVnodeLocksSummary(vnode
):
2008 """ Internal function to get summary of advisory locks for the given vnode
2009 params: vnode - value representing the vnode object
2010 return: str - formatted output information for the summary of advisory locks
2014 lockf_list
= vnode
.v_lockf
2015 for lockf_itr
in IterateLinkedList(lockf_list
, 'lf_next'):
2016 out_str
+= ("{: <4s}").format('H')
2017 out_str
+= GetVnodeLock(lockf_itr
)
2018 lockf_blocker
= lockf_itr
.lf_blkhd
.tqh_first
2019 while lockf_blocker
:
2020 out_str
+= ("{: <4s}").format('>')
2021 out_str
+= GetVnodeLock(lockf_blocker
)
2022 lockf_blocker
= lockf_blocker
.lf_block
.tqe_next
2025 @lldb_command('showvnodelocks')
2026 def ShowVnodeLocks(cmd_args
=None):
2027 """ Routine to display list of advisory record locks for the given vnode address
2028 Usage: showvnodelocks <address of vnode>
2031 print "No arguments passed"
2032 print ShowVnodeLocks
.__doc
__
2034 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
2036 print "unknown arguments:", str(cmd_args
)
2038 print GetVnodeLocksSummary
.header
2039 print GetVnodeLocksSummary(vnode_val
)
2041 # EndMacro: showvnodelocks
2043 # Macro: showproclocks
2045 @lldb_command('showproclocks')
2046 def ShowProcLocks(cmd_args
=None):
2047 """ Routine to display list of advisory record locks for the given process
2048 Usage: showproclocks <address of proc>
2051 print "No arguments passed"
2052 print ShowProcLocks
.__doc
__
2054 proc
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
2056 print "unknown arguments:", str(cmd_args
)
2059 proc_filedesc
= proc
.p_fd
2060 fd_lastfile
= proc_filedesc
.fd_lastfile
2061 fd_ofiles
= proc_filedesc
.fd_ofiles
2064 while count
<= fd_lastfile
:
2065 if fd_ofiles
[count
]:
2066 fglob
= fd_ofiles
[count
].f_fglob
2067 fo_type
= fglob
.fg_ops
.fo_type
2069 fg_data
= fglob
.fg_data
2070 fg_vnode
= Cast(fg_data
, 'vnode *')
2071 name
= fg_vnode
.v_name
2072 lockf_itr
= fg_vnode
.v_lockf
2075 print GetVnodeLocksSummary
.header
2077 out_str
+= ("\n( fd {:d}, name ").format(count
)
2079 out_str
+= "(null) )\n"
2081 out_str
+= "{:s} )\n".format(name
)
2083 print GetVnodeLocksSummary(fg_vnode
)
2085 print "\n{0: d} total locks for {1: #018x}".format(seen
, proc
)
2087 # EndMacro: showproclocks
2089 @lldb_type_summary(['vnode_t', 'vnode *'])
2090 @header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <6s} {8: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name'))
2091 def GetVnodeSummary(vnode
):
2092 """ Get a summary of important information out of vnode
2095 format_string
= "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <6s} {8: <35s}"
2096 usecount
= int(vnode
.v_usecount
)
2097 iocount
= int(vnode
.v_iocount
)
2098 v_data_ptr
= int(hex(vnode
.v_data
), 16)
2099 vtype
= int(vnode
.v_type
)
2100 vtype_str
= "%d" % vtype
2101 vnode_types
= ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
2102 if vtype
>= 0 and vtype
< len(vnode_types
):
2103 vtype_str
= vnode_types
[vtype
]
2104 parent_ptr
= int(hex(vnode
.v_parent
), 16)
2105 name_ptr
= int(hex(vnode
.v_name
), 16)
2108 name
= str(vnode
.v_name
)
2109 elif int(vnode
.v_tag
) == 16 :
2110 cnode
= Cast(vnode
.v_data
, 'cnode *')
2111 name
= "hfs: %s" % str( Cast(cnode
.c_desc
.cd_nameptr
, 'char *'))
2113 csblob_version
= '-'
2114 if (vtype
== 1) and (vnode
.v_un
.vu_ubcinfo
!= 0):
2115 csblob_version
= '{: <6d}'.format(vnode
.v_un
.vu_ubcinfo
.cs_add_gen
)
2116 # Check to see if vnode is mapped/unmapped
2117 if (vnode
.v_un
.vu_ubcinfo
.ui_flags
& 0x8) != 0:
2121 out_str
+= format_string
.format(vnode
, usecount
, iocount
, v_data_ptr
, vtype_str
, parent_ptr
, mapped
, csblob_version
, name
)
2124 @lldb_command('showallvnodes')
2125 def ShowAllVnodes(cmd_args
=None):
2126 """ Display info about all vnodes
2128 mntlist
= kern
.globals.mountlist
2129 print GetVnodeSummary
.header
2130 for mntval
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
2131 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2132 print GetVnodeSummary(vnodeval
)
2135 @lldb_command('showvnode')
2136 def ShowVnode(cmd_args
=None):
2137 """ Display info about one vnode
2138 usage: showvnode <vnode>
2140 if cmd_args
== None or len(cmd_args
) < 1:
2141 print "Please provide valid vnode argument. Type help showvnode for help."
2143 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0],'vnode *')
2144 print GetVnodeSummary
.header
2145 print GetVnodeSummary(vnodeval
)
2147 @lldb_command('showvolvnodes')
2148 def ShowVolVnodes(cmd_args
=None):
2149 """ Display info about all vnodes of a given mount_t
2151 if cmd_args
== None or len(cmd_args
) < 1:
2152 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2154 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2155 print GetVnodeSummary
.header
2156 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2157 print GetVnodeSummary(vnodeval
)
2160 @lldb_command('showvolbusyvnodes')
2161 def ShowVolBusyVnodes(cmd_args
=None):
2162 """ Display info about busy (iocount!=0) vnodes of a given mount_t
2164 if cmd_args
== None or len(cmd_args
) < 1:
2165 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2167 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2168 print GetVnodeSummary
.header
2169 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2170 if int(vnodeval
.v_iocount
) != 0:
2171 print GetVnodeSummary(vnodeval
)
2173 @lldb_command('showallbusyvnodes')
2174 def ShowAllBusyVnodes(cmd_args
=None):
2175 """ Display info about all busy (iocount!=0) vnodes
2177 mntlistval
= kern
.globals.mountlist
2178 for mntval
in IterateTAILQ_HEAD(mntlistval
, 'mnt_list'):
2179 ShowVolBusyVnodes([hex(mntval
)])
2181 @lldb_command('print_vnode')
2182 def PrintVnode(cmd_args
=None):
2183 """ Prints out the fields of a vnode struct
2184 Usage: print_vnode <vnode>
2187 print "Please provide valid vnode argument. Type help print_vnode for help."
2191 @lldb_command('showworkqvnodes')
2192 def ShowWorkqVnodes(cmd_args
=None):
2193 """ Print the vnode worker list
2194 Usage: showworkqvnodes <struct mount *>
2197 print "Please provide valid mount argument. Type help showworkqvnodes for help."
2200 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2201 vp
= Cast(mp
.mnt_workerqueue
.tqh_first
, 'vnode *')
2202 print GetVnodeSummary
.header
2204 print GetVnodeSummary(vp
)
2205 vp
= vp
.v_mntvnodes
.tqe_next
2207 @lldb_command('shownewvnodes')
2208 def ShowNewVnodes(cmd_args
=None):
2209 """ Print the new vnode list
2210 Usage: shownewvnodes <struct mount *>
2213 print "Please provide valid mount argument. Type help shownewvnodes for help."
2215 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2216 vp
= Cast(mp
.mnt_newvnodes
.tqh_first
, 'vnode *')
2217 print GetVnodeSummary
.header
2219 print GetVnodeSummary(vp
)
2220 vp
= vp
.v_mntvnodes
.tqe_next
2223 @lldb_command('showprocvnodes')
2224 def ShowProcVnodes(cmd_args
=None):
2225 """ Routine to print out all the open fds which are vnodes in a process
2226 Usage: showprocvnodes <proc *>
2229 print "Please provide valid proc argument. Type help showprocvnodes for help."
2231 procptr
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
2232 fdptr
= Cast(procptr
.p_fd
, 'filedesc *')
2233 if int(fdptr
.fd_cdir
) != 0:
2234 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_cdir
))
2235 if int(fdptr
.fd_rdir
) != 0:
2236 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_rdir
))
2238 print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary
.header
2239 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
2240 fpptr
= Cast(fdptr
.fd_ofiles
, 'uint64_t *')
2241 while count
< fdptr
.fd_nfiles
:
2242 fpp
= dereference(fpptr
)
2243 fproc
= kern
.GetValueFromAddress(int(fpp
), 'fileproc *')
2245 fglob
= dereference(fproc
).f_fglob
2247 if (int(fglob
) != 0) and (int(fglob
.fg_ops
.fo_type
) == 1):
2248 if (fdptr
.fd_ofileflags
[count
] & 1): flags
+= 'E'
2249 if (fdptr
.fd_ofileflags
[count
] & 2): flags
+= 'F'
2250 if (fdptr
.fd_ofileflags
[count
] & 4): flags
+= 'R'
2251 if (fdptr
.fd_ofileflags
[count
] & 8): flags
+= 'C'
2252 print '{0: <5d} {1: <7s} {2: <#020x} '.format(count
, flags
, fglob
) + GetVnodeSummary(Cast(fglob
.fg_data
, 'vnode *'))
2254 fpptr
= kern
.GetValueFromAddress(int(fpptr
) + kern
.ptrsize
,'uint64_t *')
2256 @lldb_command('showallprocvnodes')
2257 def ShowAllProcVnodes(cmd_args
=None):
2258 """ Routine to print out all the open fds which are vnodes
2261 procptr
= Cast(kern
.globals.allproc
.lh_first
, 'proc *')
2262 while procptr
and int(procptr
) != 0:
2263 print '{:<s}'.format("=" * 106)
2264 print GetProcInfo(procptr
)
2265 ShowProcVnodes([int(procptr
)])
2266 procptr
= procptr
.p_list
.le_next
2268 @xnudebug_test('test_vnode')
2269 def TestShowAllVnodes(kernel_target
, config
, lldb_obj
, isConnected
):
2270 """ Test the functionality of vnode related commands
2276 print "Target is not connected. Cannot test memstats"
2278 res
= lldb
.SBCommandReturnObject()
2279 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("showallvnodes", res
)
2280 result
= res
.GetOutput()
2281 if len(result
.split("\n")) > 2 and result
.find('VREG') != -1 and len(result
.splitlines()[2].split()) > 5:
2287 @lldb_type_summary(['_lck_grp_ *'])
2288 def GetMutexEntry(mtxg
):
2289 """ Summarize a mutex group entry with important information.
2291 mtxg: value - obj representing a mutex group in kernel
2293 out_string - summary of the mutex group
2297 if kern
.ptrsize
== 8:
2298 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2300 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2302 if mtxg
.lck_grp_mtxcnt
:
2303 out_string
+= format_string
.format(mtxg
, mtxg
.lck_grp_mtxcnt
,mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_util_cnt
,
2304 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_miss_cnt
,
2305 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_wait_cnt
, mtxg
.lck_grp_name
)
2308 @lldb_command('showallmtx')
2309 def ShowAllMtx(cmd_args
=None):
2310 """ Routine to print a summary listing of all mutexes
2313 if kern
.ptrsize
== 8:
2314 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2316 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2318 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2320 mtxgrp_queue_head
= kern
.globals.lck_grp_queue
2321 mtxgrp_ptr_type
= GetType('_lck_grp_ *')
2323 for mtxgrp_ptr
in IterateQueue(mtxgrp_queue_head
, mtxgrp_ptr_type
, "lck_grp_link"):
2324 print GetMutexEntry(mtxgrp_ptr
)
2326 # EndMacro: showallmtx
2328 # Macro: showallrwlck
2329 @lldb_type_summary(['_lck_grp_ *'])
2330 def GetRWLEntry(rwlg
):
2331 """ Summarize a reader writer lock group with important information.
2333 rwlg: value - obj representing a reader writer lock group in kernel
2335 out_string - summary of the reader writer lock group
2339 if kern
.ptrsize
== 8:
2340 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2342 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2344 if rwlg
.lck_grp_rwcnt
:
2345 out_string
+= format_string
.format(rwlg
, rwlg
.lck_grp_rwcnt
,rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_util_cnt
,
2346 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_miss_cnt
,
2347 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_wait_cnt
, rwlg
.lck_grp_name
)
2351 @lldb_type_summary(['lck_mtx_t *'])
2352 @header("===== Mutex Lock Summary =====")
2353 def GetMutexLockSummary(mtx
):
2354 """ Summarize mutex lock with important information.
2356 mtx: value - obj representing a mutex lock in kernel
2358 out_str - summary of the mutex lock
2361 return "Invalid lock value: 0x0"
2363 if kern
.arch
== "x86_64":
2364 out_str
= "Lock Type : MUTEX\n"
2365 if mtx
.lck_mtx_tag
== 0x07ff1007 :
2366 out_str
+= "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx
.lck_mtx_ptr
)
2367 mtx
= Cast(mtx
.lck_mtx_ptr
, 'lck_mtx_t *')
2369 if mtx
.lck_mtx_tag
== 0x07fe2007 :
2370 out_str
+= "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx
.lck_mtx_tag
)
2372 out_str
+= "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx
=mtx
)
2373 out_str
+= "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx
=mtx
)
2374 out_str
+= "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx
=mtx
)
2375 out_str
+= "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx
=mtx
)
2376 out_str
+= "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx
=mtx
)
2377 out_str
+= "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx
=mtx
)
2378 out_str
+= "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx
=mtx
)
2379 out_str
+= "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx
=mtx
)
2380 if mtx
.lck_mtx_pad32
== 0xFFFFFFFF :
2381 out_str
+= "Canary (valid) : {mtx.lck_mtx_pad32:#x}\n".format(mtx
=mtx
)
2383 out_str
+= "Canary (INVALID) : {mtx.lck_mtx_pad32:#x}\n".format(mtx
=mtx
)
2386 out_str
= "Lock Type\t\t: MUTEX\n"
2387 out_str
+= "Owner Thread\t\t: {:#x}".format(mtx
.lck_mtx_data
& ~
0x3)
2388 if (mtx
.lck_mtx_data
& ~
0x3) == 0xfffffff0:
2389 out_str
+= " Held as spinlock"
2390 out_str
+= "\nNumber of Waiters\t: {:d}\n".format(mtx
.lck_mtx_waiters
)
2391 out_str
+= "Flags\t\t\t: "
2392 if mtx
.lck_mtx_data
& 0x1:
2393 out_str
+= "[Interlock Locked] "
2394 if mtx
.lck_mtx_data
& 0x2:
2395 out_str
+= "[Wait Flag]"
2398 @lldb_type_summary(['lck_spin_t *'])
2399 @header("===== SpinLock Summary =====")
2400 def GetSpinLockSummary(spinlock
):
2401 """ Summarize spinlock with important information.
2403 spinlock: value - obj representing a spinlock in kernel
2405 out_str - summary of the spinlock
2408 return "Invalid lock value: 0x0"
2410 out_str
= "Lock Type\t\t: SPINLOCK\n"
2411 if kern
.arch
== "x86_64":
2412 out_str
+= "Interlock\t\t: {:#x}\n".format(spinlock
.interlock
)
2415 lock_data
= spinlock
.hwlock
.lock_data
2417 out_str
+= "Invalid state: interlock is locked but no owner\n"
2419 out_str
+= "Owner Thread\t\t: "
2423 out_str
+= "{:#x}\n".format(lock_data
& ~
0x1)
2424 if (lock_data
& 1) == 0:
2425 out_str
+= "Invalid state: owned but interlock bit is not set\n"
2428 @lldb_command('showlock', 'MS')
2429 def ShowLock(cmd_args
=None, cmd_options
={}):
2430 """ Show info about a lock - its state and owner thread details
2431 Usage: showlock <address of a lock>
2432 -M : to consider <addr> as lck_mtx_t
2433 -S : to consider <addr> as lck_spin_t
2436 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
2441 # from osfmk/arm/locks.h
2442 LCK_SPIN_TYPE
= 0x11
2444 if kern
.arch
== "x86_64":
2445 if "-M" in cmd_options
:
2446 lock_mtx
= kern
.GetValueFromAddress(addr
, 'lck_mtx_t *')
2447 summary_str
= GetMutexLockSummary(lock_mtx
)
2448 elif "-S" in cmd_options
:
2449 lock_spin
= kern
.GetValueFromAddress(addr
, 'lck_spin_t *')
2450 summary_str
= GetSpinLockSummary(lock_spin
)
2452 summary_str
= "Please specify supported lock option(-M/-S)"
2456 lock
= kern
.GetValueFromAddress(addr
, 'uintptr_t *')
2458 lock_mtx
= Cast(lock
, 'lck_mtx_t*')
2459 if lock_mtx
.lck_mtx_type
== LCK_MTX_TYPE
:
2460 summary_str
= GetMutexLockSummary(lock_mtx
)
2462 lock_spin
= Cast(lock
, 'lck_spin_t*')
2463 if lock_spin
.type == LCK_SPIN_TYPE
:
2464 summary_str
= GetSpinLockSummary(lock_spin
)
2465 if summary_str
== "":
2466 summary_str
= "Lock Type\t\t: INVALID LOCK"
2471 @lldb_command('showallrwlck')
2472 def ShowAllRWLck(cmd_args
=None):
2473 """ Routine to print a summary listing of all read/writer locks
2475 if kern
.ptrsize
== 8:
2476 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2478 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2480 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2482 rwlgrp_queue_head
= kern
.globals.lck_grp_queue
2483 rwlgrp_ptr_type
= GetType('_lck_grp_ *')
2484 for rwlgrp_ptr
in IterateQueue(rwlgrp_queue_head
, rwlgrp_ptr_type
, "lck_grp_link"):
2485 print GetRWLEntry(rwlgrp_ptr
)
2487 # EndMacro: showallrwlck
2489 #Macro: showbootermemorymap
2490 @lldb_command('showbootermemorymap')
2491 def ShowBooterMemoryMap(cmd_args
=None):
2492 """ Prints out the phys memory map from kernelBootArgs
2493 Supported only on x86_64
2495 if kern
.arch
!= 'x86_64':
2496 print "showbootermemorymap not supported on this architecture"
2519 boot_args
= kern
.globals.kernelBootArgs
2520 msize
= boot_args
.MemoryMapDescriptorSize
2521 mcount
= (boot_args
.MemoryMapSize
) / unsigned(msize
)
2523 out_string
+= "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2527 mptr
= kern
.GetValueFromAddress(unsigned(boot_args
.MemoryMap
) + kern
.VM_MIN_KERNEL_ADDRESS
+ unsigned(i
*msize
), 'EfiMemoryRange *')
2528 mtype
= unsigned(mptr
.Type
)
2529 if mtype
in memtype_dict
:
2530 out_string
+= "{0: <12s}".format(memtype_dict
[mtype
])
2532 out_string
+= "{0: <12s}".format("UNKNOWN")
2534 if mptr
.VirtualStart
== 0:
2535 out_string
+= "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, ' '*19, mptr
.Attribute
)
2537 out_string
+= "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, mptr
.VirtualStart
, mptr
.Attribute
)
2541 #EndMacro: showbootermemorymap
2543 @lldb_command('show_all_purgeable_objects')
2544 def ShowAllPurgeableVmObjects(cmd_args
=None):
2545 """ Routine to print a summary listing of all the purgeable vm objects
2547 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2548 ShowAllPurgeableVolatileVmObjects()
2549 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2550 ShowAllPurgeableNonVolatileVmObjects()
2552 @lldb_command('show_all_purgeable_nonvolatile_objects')
2553 def ShowAllPurgeableNonVolatileVmObjects(cmd_args
=None):
2554 """ Routine to print a summary listing of all the vm objects in
2555 the purgeable_nonvolatile_queue
2558 nonvolatile_total
= lambda:None
2559 nonvolatile_total
.objects
= 0
2560 nonvolatile_total
.vsize
= 0
2561 nonvolatile_total
.rsize
= 0
2562 nonvolatile_total
.wsize
= 0
2563 nonvolatile_total
.csize
= 0
2564 nonvolatile_total
.disowned_objects
= 0
2565 nonvolatile_total
.disowned_vsize
= 0
2566 nonvolatile_total
.disowned_rsize
= 0
2567 nonvolatile_total
.disowned_wsize
= 0
2568 nonvolatile_total
.disowned_csize
= 0
2570 queue_len
= kern
.globals.purgeable_nonvolatile_count
2571 queue_head
= kern
.globals.purgeable_nonvolatile_queue
2573 print 'purgeable_nonvolatile_queue:{: <#018x} purgeable_volatile_count:{:d}\n'.format(kern
.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len
)
2574 print 'N:non-volatile V:volatile E:empty D:deny\n'
2576 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")
2578 for object in IterateQueue(queue_head
, 'struct vm_object *', 'objq'):
2580 ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
)
2581 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
)
2582 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
)
2585 def ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
):
2586 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
2588 object - core.value : a object of type 'struct vm_object *'
2592 page_size
= kern
.globals.page_size
2593 if object.purgable
== 0:
2595 elif object.purgable
== 1:
2597 elif object.purgable
== 2:
2599 elif object.purgable
== 3:
2603 if object.pager
== 0:
2604 compressed_count
= 0
2606 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
2607 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
2609 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
))
2611 nonvolatile_total
.objects
+= 1
2612 nonvolatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
2613 nonvolatile_total
.rsize
+= object.resident_page_count
2614 nonvolatile_total
.wsize
+= object.wired_page_count
2615 nonvolatile_total
.csize
+= compressed_count
2616 if object.vo_un2
.vou_owner
== 0:
2617 nonvolatile_total
.disowned_objects
+= 1
2618 nonvolatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
2619 nonvolatile_total
.disowned_rsize
+= object.resident_page_count
2620 nonvolatile_total
.disowned_wsize
+= object.wired_page_count
2621 nonvolatile_total
.disowned_csize
+= compressed_count
2624 @lldb_command('show_all_purgeable_volatile_objects')
2625 def ShowAllPurgeableVolatileVmObjects(cmd_args
=None):
2626 """ Routine to print a summary listing of all the vm objects in
2627 the purgeable queues
2629 volatile_total
= lambda:None
2630 volatile_total
.objects
= 0
2631 volatile_total
.vsize
= 0
2632 volatile_total
.rsize
= 0
2633 volatile_total
.wsize
= 0
2634 volatile_total
.csize
= 0
2635 volatile_total
.disowned_objects
= 0
2636 volatile_total
.disowned_vsize
= 0
2637 volatile_total
.disowned_rsize
= 0
2638 volatile_total
.disowned_wsize
= 0
2639 volatile_total
.disowned_csize
= 0
2641 purgeable_queues
= kern
.globals.purgeable_queues
2642 print "---------- OBSOLETE\n"
2643 ShowPurgeableQueue(purgeable_queues
[0], volatile_total
)
2644 print "\n\n---------- FIFO\n"
2645 ShowPurgeableQueue(purgeable_queues
[1], volatile_total
)
2646 print "\n\n---------- LIFO\n"
2647 ShowPurgeableQueue(purgeable_queues
[2], volatile_total
)
2649 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
)
2650 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
)
2651 purgeable_count
= kern
.globals.vm_page_purgeable_count
2652 purgeable_wired_count
= kern
.globals.vm_page_purgeable_wired_count
2653 if purgeable_count
!= volatile_total
.rsize
or purgeable_wired_count
!= volatile_total
.wsize
:
2654 mismatch
= "<--------- MISMATCH\n"
2657 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count
, purgeable_wired_count
, mismatch
)
2660 def ShowPurgeableQueue(qhead
, volatile_total
):
2661 print "----- GROUP 0\n"
2662 ShowPurgeableGroup(qhead
.objq
[0], volatile_total
)
2663 print "----- GROUP 1\n"
2664 ShowPurgeableGroup(qhead
.objq
[1], volatile_total
)
2665 print "----- GROUP 2\n"
2666 ShowPurgeableGroup(qhead
.objq
[2], volatile_total
)
2667 print "----- GROUP 3\n"
2668 ShowPurgeableGroup(qhead
.objq
[3], volatile_total
)
2669 print "----- GROUP 4\n"
2670 ShowPurgeableGroup(qhead
.objq
[4], volatile_total
)
2671 print "----- GROUP 5\n"
2672 ShowPurgeableGroup(qhead
.objq
[5], volatile_total
)
2673 print "----- GROUP 6\n"
2674 ShowPurgeableGroup(qhead
.objq
[6], volatile_total
)
2675 print "----- GROUP 7\n"
2676 ShowPurgeableGroup(qhead
.objq
[7], volatile_total
)
2678 def ShowPurgeableGroup(qhead
, volatile_total
):
2680 for object in IterateQueue(qhead
, 'struct vm_object *', 'objq'):
2682 # 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","")
2683 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")
2685 ShowPurgeableVolatileVmObject(object, idx
, volatile_total
)
2687 def ShowPurgeableVolatileVmObject(object, idx
, volatile_total
):
2688 """ Routine to print out a summary a VM object in a purgeable queue
2690 object - core.value : a object of type 'struct vm_object *'
2694 ## if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
2698 page_size
= kern
.globals.page_size
2699 if object.purgable
== 0:
2701 elif object.purgable
== 1:
2703 elif object.purgable
== 2:
2705 elif object.purgable
== 3:
2709 if object.pager
== 0:
2710 compressed_count
= 0
2712 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
2713 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
2714 # 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)
2715 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
))
2716 volatile_total
.objects
+= 1
2717 volatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
2718 volatile_total
.rsize
+= object.resident_page_count
2719 volatile_total
.wsize
+= object.wired_page_count
2720 volatile_total
.csize
+= compressed_count
2721 if object.vo_un2
.vou_owner
== 0:
2722 volatile_total
.disowned_objects
+= 1
2723 volatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
2724 volatile_total
.disowned_rsize
+= object.resident_page_count
2725 volatile_total
.disowned_wsize
+= object.wired_page_count
2726 volatile_total
.disowned_csize
+= compressed_count
2729 def GetCompressedPagesForObject(obj
):
2732 pager
= Cast(obj
.pager
, 'compressor_pager_t')
2733 return pager
.cpgr_num_slots_occupied
2734 """ # commented code below
2735 if pager.cpgr_num_slots > 128:
2736 slots_arr = pager.cpgr_slots.cpgr_islots
2737 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
2740 compressed_pages = 0
2741 while index < num_indirect_slot_ptr:
2743 if slots_arr[index]:
2744 while compressor_slot < 128:
2745 if slots_arr[index][compressor_slot]:
2746 compressed_pages += 1
2747 compressor_slot += 1
2750 slots_arr = pager.cpgr_slots.cpgr_dslots
2752 compressed_pages = 0
2753 while compressor_slot < pager.cpgr_num_slots:
2754 if slots_arr[compressor_slot]:
2755 compressed_pages += 1
2756 compressor_slot += 1
2757 return compressed_pages
2760 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
2761 """ Routine to print out a summary listing of all the entries in a vm_map
2763 task - core.value : a object of type 'task *'
2767 print "vm_map entries for task " + hex(task
)
2768 print GetTaskSummary
.header
2769 print GetTaskSummary(task
)
2771 print "Task {0: <#020x} has map = 0x0"
2773 showmapvme(task
.map, 0, 0, show_pager_info
, show_all_shadows
, False)
2775 @lldb_command("showmapvme", "A:B:F:PRST")
2776 def ShowMapVME(cmd_args
=None, cmd_options
={}):
2777 """Routine to print out info about the specified vm_map and its vm entries
2778 usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
2779 Use -A <start> flag to start at virtual address <start>
2780 Use -B <end> flag to end at virtual address <end>
2781 Use -F <virtaddr> flag to find just the VME containing the given VA
2782 Use -S flag to show VM object shadow chains
2783 Use -P flag to show pager info (mapped file, compressed pages, ...)
2784 Use -R flag to reverse order
2785 Use -T to show red-black tree pointers
2787 if cmd_args
== None or len(cmd_args
) < 1:
2788 print "Invalid argument.", ShowMapVME
.__doc
__
2790 show_pager_info
= False
2791 show_all_shadows
= False
2792 show_rb_tree
= False
2795 reverse_order
= False
2796 if "-A" in cmd_options
:
2797 start_vaddr
= unsigned(int(cmd_options
['-A'], 16))
2798 if "-B" in cmd_options
:
2799 end_vaddr
= unsigned(int(cmd_options
['-B'], 16))
2800 if "-F" in cmd_options
:
2801 start_vaddr
= unsigned(int(cmd_options
['-F'], 16))
2802 end_vaddr
= start_vaddr
2803 if "-P" in cmd_options
:
2804 show_pager_info
= True
2805 if "-S" in cmd_options
:
2806 show_all_shadows
= True
2807 if "-R" in cmd_options
:
2808 reverse_order
= True
2809 if "-T" in cmd_options
:
2811 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
2812 showmapvme(map, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
2814 @lldb_command("showvmobject", "A:B:PRST")
2815 def ShowVMObject(cmd_args
=None, cmd_options
={}):
2816 """Routine to print out a VM object and its shadow chain
2817 usage: showvmobject <vm_object> [-S] [-P]
2818 -S: show VM object shadow chain
2819 -P: show pager info (mapped file, compressed pages, ...)
2821 if cmd_args
== None or len(cmd_args
) < 1:
2822 print "Invalid argument.", ShowMapVME
.__doc
__
2824 show_pager_info
= False
2825 show_all_shadows
= False
2826 if "-P" in cmd_options
:
2827 show_pager_info
= True
2828 if "-S" in cmd_options
:
2829 show_all_shadows
= True
2830 object = kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
2831 showvmobject(object, 0, 0, show_pager_info
, show_all_shadows
)
2833 def showvmobject(object, offset
=0, size
=0, show_pager_info
=False, show_all_shadows
=False):
2834 page_size
= kern
.globals.page_size
2835 vnode_pager_ops
= kern
.globals.vnode_pager_ops
2836 vnode_pager_ops_addr
= unsigned(addressof(vnode_pager_ops
))
2838 if size
== 0 and object != 0 and object.internal
:
2839 size
= object.vo_un1
.vou_size
2842 if show_all_shadows
== False and depth
!= 1 and object.shadow
!= 0:
2843 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
2844 object = object.shadow
2846 if object.copy_strategy
== 0:
2848 elif object.copy_strategy
== 2:
2850 elif object.copy_strategy
== 4:
2854 copy_strategy
=str(object.copy_strategy
)
2856 internal
= "internal"
2858 internal
= "external"
2859 purgeable
= "NVED"[int(object.purgable
)]
2861 if object.phys_contiguous
:
2862 pager_string
= pager_string
+ "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2
.vou_shadow_offset
), unsigned(object.vo_un1
.vou_size
))
2863 pager
= object.pager
2864 if show_pager_info
and pager
!= 0:
2866 pager_string
= pager_string
+ "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
2867 elif unsigned(pager
.mo_pager_ops
) == vnode_pager_ops_addr
:
2868 vnode_pager
= Cast(pager
,'vnode_pager *')
2869 pager_string
= pager_string
+ "-> " + GetVnodePath(vnode_pager
.vnode_handle
)
2871 pager_string
= pager_string
+ "-> {:s}:{: <#018x}".format(pager
.mo_pager_ops
.memory_object_pager_name
, pager
)
2872 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
)
2873 # 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)
2874 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
2875 object = object.shadow
2877 def showmapvme(map, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
=False, show_rb_tree
=False):
2880 rsize
= int(map.pmap
.stats
.resident_count
)
2881 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
2882 print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(map,map.pmap
,unsigned(map.size
),map.hdr
.nentries
,rsize
,map.hdr
.links
.start
,map.hdr
.links
.end
)
2883 showmaphdrvme(map.hdr
, map.pmap
, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
2885 def showmapcopyvme(mapcopy
, start_vaddr
=0, end_vaddr
=0, show_pager_info
=True, show_all_shadows
=True, reverse_order
=False, show_rb_tree
=False):
2886 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map_copy","pmap","size","#ents","rsize","start","end")
2887 print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(mapcopy
,0,0,mapcopy
.c_u
.hdr
.nentries
,0,mapcopy
.c_u
.hdr
.links
.start
,mapcopy
.c_u
.hdr
.links
.end
)
2888 showmaphdrvme(mapcopy
.c_u
.hdr
, 0, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
2890 def showmaphdrvme(maphdr
, pmap
, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
):
2891 page_size
= kern
.globals.page_size
2892 vnode_pager_ops
= kern
.globals.vnode_pager_ops
2893 vnode_pager_ops_addr
= unsigned(addressof(vnode_pager_ops
))
2894 if hasattr(kern
.globals, 'compressor_object'):
2895 compressor_object
= kern
.globals.compressor_object
2897 compressor_object
= -1;
2898 vme_list_head
= maphdr
.links
2899 vme_ptr_type
= GetType('vm_map_entry *')
2900 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
2901 last_end
= unsigned(maphdr
.links
.start
)
2903 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links", reverse_order
):
2904 if start_vaddr
!= 0 and end_vaddr
!= 0:
2905 if unsigned(vme
.links
.start
) > end_vaddr
:
2907 if unsigned(vme
.links
.end
) <= start_vaddr
:
2908 last_end
= unsigned(vme
.links
.end
)
2909 skipped_entries
= skipped_entries
+ 1
2911 if skipped_entries
!= 0:
2912 print "... skipped {:d} entries ...".format(skipped_entries
)
2914 if unsigned(vme
.links
.start
) != last_end
:
2915 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,vme
.links
.start
,(unsigned(vme
.links
.start
) - last_end
)/page_size
)
2916 last_end
= unsigned(vme
.links
.end
)
2917 size
= unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)
2918 object = vme
.vme_object
.vmo_object
2920 object_str
= "{: <#018x}".format(object)
2921 elif vme
.is_sub_map
:
2922 if object == kern
.globals.bufferhdr_map
:
2923 object_str
= "BUFFERHDR_MAP"
2924 elif object == kern
.globals.mb_map
:
2925 object_str
= "MB_MAP"
2926 elif object == kern
.globals.bsd_pageable_map
:
2927 object_str
= "BSD_PAGEABLE_MAP"
2928 elif object == kern
.globals.ipc_kernel_map
:
2929 object_str
= "IPC_KERNEL_MAP"
2930 elif object == kern
.globals.ipc_kernel_copy_map
:
2931 object_str
= "IPC_KERNEL_COPY_MAP"
2932 elif object == kern
.globals.kalloc_map
:
2933 object_str
= "KALLOC_MAP"
2934 elif object == kern
.globals.zone_map
:
2935 object_str
= "ZONE_MAP"
2936 elif hasattr(kern
.globals, 'compressor_map') and object == kern
.globals.compressor_map
:
2937 object_str
= "COMPRESSOR_MAP"
2938 elif hasattr(kern
.globals, 'gzalloc_map') and object == kern
.globals.gzalloc_map
:
2939 object_str
= "GZALLOC_MAP"
2940 elif hasattr(kern
.globals, 'g_kext_map') and object == kern
.globals.g_kext_map
:
2941 object_str
= "G_KEXT_MAP"
2942 elif hasattr(kern
.globals, 'vector_upl_submap') and object == kern
.globals.vector_upl_submap
:
2943 object_str
= "VECTOR_UPL_SUBMAP"
2945 object_str
= "submap:{: <#018x}".format(object)
2947 if object == kern
.globals.kernel_object
:
2948 object_str
= "KERNEL_OBJECT"
2949 elif object == kern
.globals.vm_submap_object
:
2950 object_str
= "VM_SUBMAP_OBJECT"
2951 elif object == compressor_object
:
2952 object_str
= "COMPRESSOR_OBJECT"
2954 object_str
= "{: <#018x}".format(object)
2955 offset
= unsigned(vme
.vme_offset
) & ~
0xFFF
2956 tag
= unsigned(vme
.vme_offset
& 0xFFF)
2958 if vme
.protection
& 0x1:
2962 if vme
.protection
& 0x2:
2966 if vme
.protection
& 0x4:
2971 if vme
.max_protection
& 0x1:
2972 max_protection
+="r"
2974 max_protection
+= "-"
2975 if vme
.max_protection
& 0x2:
2976 max_protection
+= "w"
2978 max_protection
+= "-"
2979 if vme
.max_protection
& 0x4:
2980 max_protection
+= "x"
2982 max_protection
+= "-"
2992 if vme
.used_for_jit
:
2995 if pmap
== kern
.globals.kernel_pmap
:
2996 xsite
= Cast(kern
.globals.vm_allocation_sites
[tag
],'OSKextAccount *')
2997 if xsite
and xsite
.site
.flags
& 0x0200:
2998 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
3001 rb_info
= "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme
.store
.entry
.rbe_left
, vme
.store
.entry
.rbe_right
, vme
.store
.entry
.rbe_parent
)
3002 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
)
3003 if (show_pager_info
or show_all_shadows
) and vme
.is_sub_map
== 0 and vme
.vme_object
.vmo_object
!= 0:
3004 object = vme
.vme_object
.vmo_object
3007 showvmobject(object, offset
, size
, show_pager_info
, show_all_shadows
)
3008 if start_vaddr
!= 0 or end_vaddr
!= 0:
3010 elif unsigned(maphdr
.links
.end
) > last_end
:
3011 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,maphdr
.links
.end
,(unsigned(maphdr
.links
.end
) - last_end
)/page_size
)
3014 def CountMapTags(map, tagcounts
, slow
):
3015 page_size
= unsigned(kern
.globals.page_size
)
3016 vme_list_head
= map.hdr
.links
3017 vme_ptr_type
= GetType('vm_map_entry *')
3018 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
3019 object = vme
.vme_object
.vmo_object
3020 tag
= vme
.vme_offset
& 0xFFF
3021 if object == kern
.globals.kernel_object
:
3024 count
= unsigned(vme
.links
.end
- vme
.links
.start
) / page_size
3026 addr
= unsigned(vme
.links
.start
)
3027 while addr
< unsigned(vme
.links
.end
):
3028 hash_id
= _calc_vm_page_hash(object, addr
)
3029 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3030 page
= _vm_page_unpack_ptr(page_list
)
3032 vmpage
= kern
.GetValueFromAddress(page
, 'vm_page_t')
3033 if (addr
== unsigned(vmpage
.vmp_offset
)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage
.vmp_object
))):
3034 if (not vmpage
.vmp_local
) and (vmpage
.vmp_wire_count
> 0):
3037 page
= _vm_page_unpack_ptr(vmpage
.vmp_next_m
)
3039 tagcounts
[tag
] += count
3040 elif vme
.is_sub_map
:
3041 CountMapTags(Cast(object,'vm_map_t'), tagcounts
, slow
)
3044 def CountWiredObject(object, tagcounts
):
3045 tagcounts
[unsigned(object.wire_tag
)] += object.wired_page_count
3048 def GetKmodIDName(kmod_id
):
3049 kmod_val
= kern
.globals.kmod
3050 for kmod
in IterateLinkedList(kmod_val
, 'next'):
3051 if (kmod
.id == kmod_id
):
3052 return "{:<50s}".format(kmod
.name
)
3056 0: "VM_KERN_MEMORY_NONE",
3057 1: "VM_KERN_MEMORY_OSFMK",
3058 2: "VM_KERN_MEMORY_BSD",
3059 3: "VM_KERN_MEMORY_IOKIT",
3060 4: "VM_KERN_MEMORY_LIBKERN",
3061 5: "VM_KERN_MEMORY_OSKEXT",
3062 6: "VM_KERN_MEMORY_KEXT",
3063 7: "VM_KERN_MEMORY_IPC",
3064 8: "VM_KERN_MEMORY_STACK",
3065 9: "VM_KERN_MEMORY_CPU",
3066 10: "VM_KERN_MEMORY_PMAP",
3067 11: "VM_KERN_MEMORY_PTE",
3068 12: "VM_KERN_MEMORY_ZONE",
3069 13: "VM_KERN_MEMORY_KALLOC",
3070 14: "VM_KERN_MEMORY_COMPRESSOR",
3071 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
3072 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
3073 17: "VM_KERN_MEMORY_WAITQ",
3074 18: "VM_KERN_MEMORY_DIAG",
3075 19: "VM_KERN_MEMORY_LOG",
3076 20: "VM_KERN_MEMORY_FILE",
3077 21: "VM_KERN_MEMORY_MBUF",
3078 22: "VM_KERN_MEMORY_UBC",
3079 23: "VM_KERN_MEMORY_SECURITY",
3080 24: "VM_KERN_MEMORY_MLOCK",
3081 25: "VM_KERN_MEMORY_REASON",
3082 26: "VM_KERN_MEMORY_SKYWALK",
3083 27: "VM_KERN_MEMORY_LTABLE",
3084 255:"VM_KERN_MEMORY_ANY",
3087 def GetVMKernName(tag
):
3088 """ returns the formatted name for a vmtag and
3089 the sub-tag for kmod tags.
3091 if ((tag
<= 27) or (tag
== 255)):
3092 return (FixedTags
[tag
], "")
3093 site
= kern
.globals.vm_allocation_sites
[tag
]
3095 if site
.flags
& 0x007F:
3096 cstr
= addressof(site
.subtotals
[site
.subtotalscount
])
3097 return ("{:<50s}".format(str(Cast(cstr
, 'char *'))), "")
3099 if site
.flags
& 0x0200:
3100 xsite
= Cast(site
,'OSKextAccount *')
3101 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
3102 return (GetKmodIDName(xsite
.loadTag
), tagstr
);
3104 return (kern
.Symbolicate(site
), "")
3107 @lldb_command("showvmtags", "AS")
3108 def showvmtags(cmd_args
=None, cmd_options
={}):
3109 """Routine to print out info about kernel wired page allocations
3111 iterates kernel map and vm objects totaling allocations by tag.
3112 usage: showvmtags -S
3113 also iterates kernel object pages individually - slow.
3114 usage: showvmtags -A
3115 show all tags, even tags that have no wired count
3118 if "-S" in cmd_options
:
3121 if "-A" in cmd_options
:
3123 page_size
= unsigned(kern
.globals.page_size
)
3124 nsites
= unsigned(kern
.globals.vm_allocation_tag_highest
) + 1
3125 tagcounts
= [0] * nsites
3126 tagpeaks
= [0] * nsites
3127 tagmapped
= [0] * nsites
3129 if kern
.globals.vm_tag_active_update
:
3130 for tag
in range(nsites
):
3131 site
= kern
.globals.vm_allocation_sites
[tag
]
3133 tagcounts
[tag
] = unsigned(site
.total
)
3134 tagmapped
[tag
] = unsigned(site
.mapped
)
3135 tagpeaks
[tag
] = unsigned(site
.peak
)
3137 queue_head
= kern
.globals.vm_objects_wired
3138 for object in IterateQueue(queue_head
, 'struct vm_object *', 'wired_objq'):
3139 if object != kern
.globals.kernel_object
:
3140 CountWiredObject(object, tagcounts
)
3142 CountMapTags(kern
.globals.kernel_map
, tagcounts
, slow
)
3146 print " vm_allocation_tag_highest: {:<7d} ".format(nsites
- 1)
3147 print " {:<7s} {:>7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "peak", "size", "mapped", "name")
3148 for tag
in range(nsites
):
3149 if all_tags
or tagcounts
[tag
] or tagmapped
[tag
]:
3150 total
+= tagcounts
[tag
]
3151 totalmapped
+= tagmapped
[tag
]
3152 (sitestr
, tagstr
) = GetVMKernName(tag
)
3153 site
= kern
.globals.vm_allocation_sites
[tag
]
3154 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:>7d}K {:<50s}".format(tag
, tagstr
, tagpeaks
[tag
] / 1024, tagcounts
[tag
] / 1024, tagmapped
[tag
] / 1024, sitestr
)
3156 for sub
in range(site
.subtotalscount
):
3157 alloctag
= unsigned(site
.subtotals
[sub
].tag
)
3158 amount
= unsigned(site
.subtotals
[sub
].total
)
3159 subsite
= kern
.globals.vm_allocation_sites
[alloctag
]
3160 if alloctag
and subsite
:
3161 if ((subsite
.flags
& 0x007f) == 0):
3165 (sitestr
, tagstr
) = GetVMKernName(alloctag
)
3166 print " {:>7s} {:>7s} {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", " ", " ", amount
/ 1024, kind_str
, alloctag
, tagstr
, sitestr
)
3168 print "Total: {:>7d}K {:>7d}K".format(total
/ 1024, totalmapped
/ 1024)
3172 def FindVMEntriesForVnode(task
, vn
):
3173 """ returns an array of vme that have the vnode set to defined vnode
3174 each entry in array is of format (vme, start_addr, end_address, protection)
3179 pager_ops_addr
= unsigned(addressof(kern
.globals.vnode_pager_ops
))
3180 debuglog("pager_ops_addr %s" % hex(pager_ops_addr
))
3182 if unsigned(pmap
) == 0:
3184 vme_list_head
= vmmap
.hdr
.links
3185 vme_ptr_type
= gettype('vm_map_entry *')
3186 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, 'links'):
3188 if unsigned(vme
.is_sub_map
) == 0 and unsigned(vme
.vme_object
.vmo_object
) != 0:
3189 obj
= vme
.vme_object
.vmo_object
3198 vn_pager
= Cast(obj
.pager
, 'vnode_pager *')
3199 if unsigned(vn_pager
.vn_pgr_hdr
.mo_pager_ops
) == pager_ops_addr
and unsigned(vn_pager
.vnode_handle
) == unsigned(vn
):
3200 retval
.append((vme
, unsigned(vme
.links
.start
), unsigned(vme
.links
.end
), unsigned(vme
.protection
)))
3204 @lldb_command('showtaskloadinfo')
3205 def ShowTaskLoadInfo(cmd_args
=None, cmd_options
={}):
3206 """ Print the load address and uuid for the process
3207 Usage: (lldb)showtaskloadinfo <task_t>
3210 raise ArgumentError("Insufficient arguments")
3211 t
= kern
.GetValueFromAddress(cmd_args
[0], 'struct task *')
3212 print_format
= "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
3213 p
= Cast(t
.bsd_info
, 'struct proc *')
3215 uuid_out_string
= "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a
=uuid
)
3216 filepath
= GetVnodePath(p
.p_textvp
)
3217 libname
= filepath
.split('/')[-1]
3218 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
3219 mappings
= FindVMEntriesForVnode(t
, p
.p_textvp
)
3226 #print "Load address: %s" % hex(m[1])
3227 print print_format
.format(load_addr
, end_addr
, libname
, uuid_out_string
, filepath
)
3230 @header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3231 @lldb_command('vmpagelookup')
3232 def VMPageLookup(cmd_args
=None):
3233 """ Print the pages in the page bucket corresponding to the provided object and offset.
3234 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3236 if cmd_args
== None or len(cmd_args
) < 2:
3237 raise ArgumentError("Please specify an object and offset.")
3238 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3240 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3241 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3243 hash_id
= _calc_vm_page_hash(obj
, off
)
3245 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3246 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(page_list
)))
3248 print VMPageLookup
.header
3249 page
= _vm_page_unpack_ptr(page_list
)
3251 pg_t
= kern
.GetValueFromAddress(page
, 'vm_page_t')
3252 print format_string
.format(page
, pg_t
.vmp_offset
, _vm_page_unpack_ptr(pg_t
.vmp_object
))
3253 page
= _vm_page_unpack_ptr(pg_t
.vmp_next_m
)
3257 @lldb_command('vmpage_get_phys_page')
3258 def VmPageGetPhysPage(cmd_args
=None):
3259 """ return the physical page for a vm_page_t
3260 usage: vm_page_get_phys_page <vm_page_t>
3262 if cmd_args
== None or len(cmd_args
) < 1:
3263 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
3266 page
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_page_t')
3267 phys_page
= _vm_page_get_phys_page(page
)
3268 print("phys_page = 0x%x\n" % phys_page
)
3271 def _vm_page_get_phys_page(page
):
3272 if kern
.arch
== 'x86_64':
3273 return page
.vmp_phys_page
3279 if m
>= unsigned(kern
.globals.vm_page_array_beginning_addr
) and m
< unsigned(kern
.globals.vm_page_array_ending_addr
) :
3280 return (m
- unsigned(kern
.globals.vm_page_array_beginning_addr
)) / sizeof('struct vm_page') + unsigned(kern
.globals.vm_first_phys_ppnum
)
3282 page_with_ppnum
= Cast(page
, 'uint32_t *')
3283 ppnum_offset
= sizeof('struct vm_page') / sizeof('uint32_t')
3284 return page_with_ppnum
[ppnum_offset
]
3287 @lldb_command('vmpage_unpack_ptr')
3288 def VmPageUnpackPtr(cmd_args
=None):
3289 """ unpack a pointer
3290 usage: vm_page_unpack_ptr <packed_ptr>
3292 if cmd_args
== None or len(cmd_args
) < 1:
3293 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
3296 packed
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long')
3297 unpacked
= _vm_page_unpack_ptr(packed
)
3298 print("unpacked pointer = 0x%x\n" % unpacked
)
3301 def _vm_page_unpack_ptr(page
):
3302 if kern
.ptrsize
== 4 :
3308 min_addr
= kern
.globals.vm_min_kernel_and_kext_address
3309 ptr_shift
= kern
.globals.vm_packed_pointer_shift
3310 ptr_mask
= kern
.globals.vm_packed_from_vm_pages_array_mask
3311 #INTEL - min_addr = 0xffffff7f80000000
3312 #ARM - min_addr = 0x80000000
3313 #ARM64 - min_addr = 0xffffff8000000000
3314 if unsigned(page
) & unsigned(ptr_mask
) :
3315 masked_page
= (unsigned(page
) & ~ptr_mask
)
3316 # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
3317 vm_pages_addr
= unsigned(addressof(kern
.globals.vm_pages
[0]))
3318 element_size
= unsigned(addressof(kern
.globals.vm_pages
[1])) - vm_pages_addr
3319 return (vm_pages_addr
+ masked_page
* element_size
)
3320 return ((unsigned(page
) << unsigned(ptr_shift
)) + unsigned(min_addr
))
3322 @lldb_command('calcvmpagehash')
3323 def CalcVMPageHash(cmd_args
=None):
3324 """ Get the page bucket corresponding to the provided object and offset.
3325 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3327 if cmd_args
== None or len(cmd_args
) < 2:
3328 raise ArgumentError("Please specify an object and offset.")
3330 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3331 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3333 hash_id
= _calc_vm_page_hash(obj
, off
)
3335 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(kern
.globals.vm_page_buckets
[hash_id
].page_list
)))
3338 def _calc_vm_page_hash(obj
, off
):
3339 bucket_hash
= (int) (kern
.globals.vm_page_bucket_hash
)
3340 hash_mask
= (int) (kern
.globals.vm_page_hash_mask
)
3342 one
= (obj
* bucket_hash
) & 0xFFFFFFFF
3343 two
= off
>> unsigned(kern
.globals.page_shift
)
3344 three
= two ^ bucket_hash
3346 hash_id
= four
& hash_mask
3350 def AddressIsFromZoneMap(addr
):
3351 zone_map_min_address
= kern
.GetGlobalVariable('zone_map_min_address')
3352 zone_map_max_address
= kern
.GetGlobalVariable('zone_map_max_address')
3353 if (unsigned(addr
) >= unsigned(zone_map_min_address
)) and (unsigned(addr
) < unsigned(zone_map_max_address
)):
3358 def ElementOffsetInForeignPage():
3359 zone_element_alignment
= 32 # defined in zalloc.c
3360 zone_page_metadata_size
= sizeof('struct zone_page_metadata')
3361 if zone_page_metadata_size
% zone_element_alignment
== 0:
3362 offset
= zone_page_metadata_size
3364 offset
= zone_page_metadata_size
+ (zone_element_alignment
- (zone_page_metadata_size
% zone_element_alignment
))
3365 return unsigned(offset
)
3367 def ElementStartAddrFromZonePageMetadata(page_metadata
):
3368 zone_metadata_region_min
= kern
.GetGlobalVariable('zone_metadata_region_min')
3369 zone_map_min_address
= kern
.GetGlobalVariable('zone_map_min_address')
3370 page_size
= kern
.GetGlobalVariable('page_size')
3371 if AddressIsFromZoneMap(page_metadata
):
3372 page_index
= (unsigned(page_metadata
) - unsigned(zone_metadata_region_min
)) / sizeof('struct zone_page_metadata')
3373 element_start_addr
= unsigned(zone_map_min_address
) + unsigned(page_index
* page_size
)
3375 element_start_addr
= unsigned(page_metadata
) + unsigned(ElementOffsetInForeignPage())
3377 return element_start_addr
3379 def ZonePageStartAddrFromZonePageMetadata(page_metadata
):
3380 zone_metadata_region_min
= kern
.GetGlobalVariable('zone_metadata_region_min')
3381 zone_map_min_address
= kern
.GetGlobalVariable('zone_map_min_address')
3382 page_size
= kern
.GetGlobalVariable('page_size')
3384 if AddressIsFromZoneMap(page_metadata
):
3385 page_index
= (unsigned(page_metadata
) - unsigned(zone_metadata_region_min
)) / sizeof('struct zone_page_metadata')
3386 zone_page_addr
= unsigned(zone_map_min_address
) + unsigned(page_index
* page_size
)
3388 zone_page_addr
= unsigned(page_metadata
)
3390 return unsigned(zone_page_addr
)
3392 def CreateFreeElementsList(zone
, first_free
):
3394 if unsigned(first_free
) == 0:
3395 return free_elements
3396 current
= first_free
3398 free_elements
.append(unsigned(current
))
3399 next
= dereference(Cast(current
, 'vm_offset_t *'))
3400 next
= (unsigned(next
) ^
unsigned(kern
.globals.zp_nopoison_cookie
))
3401 next
= kern
.GetValueFromAddress(next
, 'vm_offset_t *')
3402 if unsigned(next
) == 0:
3404 current
= Cast(next
, 'void *')
3406 return free_elements
3408 #Macro: showallocatedzoneelement
3409 @lldb_command('showallocatedzoneelement')
3410 def ShowAllocatedElementsInZone(cmd_args
=None, cmd_options
={}):
3411 """ Show all the allocated elements in a zone
3412 usage: showzoneallocelements <address of zone>
3414 if len(cmd_args
) < 1:
3415 raise ArgumentError("Please specify a zone")
3417 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
3418 elements
= FindAllocatedElementsInZone(zone
)
3420 for elem
in elements
:
3421 print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i
, len(elements
), elem
)
3424 #EndMacro: showallocatedzoneelement
3426 def FindAllocatedElementsInZone(zone
):
3427 page_size
= kern
.GetGlobalVariable('page_size')
3429 page_queues
= ["any_free_foreign", "intermediate", "all_used"]
3432 for queue
in page_queues
:
3434 if queue
== "any_free_foreign" and unsigned(zone
.allows_foreign
) != 1:
3437 for zone_page_metadata
in IterateQueue(zone
.pages
.__getattr
__(queue
), 'struct zone_page_metadata *', 'pages'):
3439 first_free_element
= kern
.GetValueFromAddress(GetFreeList(zone_page_metadata
))
3440 free_elements
= CreateFreeElementsList(zone
, first_free_element
)
3442 chunk_page_count
= zone_page_metadata
.page_count
3443 element_addr_start
= ElementStartAddrFromZonePageMetadata(zone_page_metadata
)
3444 zone_page_start
= ZonePageStartAddrFromZonePageMetadata(zone_page_metadata
)
3445 next_page
= zone_page_start
+ page_size
3446 element_addr_end
= zone_page_start
+ (chunk_page_count
* page_size
)
3447 elem
= unsigned(element_addr_start
)
3448 while elem
< element_addr_end
:
3449 if elem
not in free_elements
:
3450 elements
.append(elem
)
3452 elem
+= zone
.elem_size
3454 if queue
== "any_free_foreign":
3455 if (elem
+ zone
.elem_size
) >= next_page
:
3456 zone_page_start
= unsigned((elem
+ page_size
) & ~
(page_size
- 1))
3457 next_page
= zone_page_start
+ page_size
3458 elem
= zone_page_start
+ unsigned(ElementOffsetInForeignPage())
3460 found_total
+= found_in_queue
3461 # print "Found {0: <d} allocated elements in the {1: <s} page queue".format(found_in_queue, queue)
3463 # print "Total number of allocated elements: {0: <d} in zone {1: <s}".format(found_total, zone.zone_name)
3466 def match_vm_page_attributes(page
, matching_attributes
):
3467 page_ptr
= addressof(page
)
3468 unpacked_vm_object
= _vm_page_unpack_ptr(page
.vmp_object
)
3469 matched_attributes
= 0
3470 if "vmp_q_state" in matching_attributes
and (page
.vmp_q_state
== matching_attributes
["vmp_q_state"]):
3471 matched_attributes
+= 1
3472 if "vm_object" in matching_attributes
and (unsigned(unpacked_vm_object
) == unsigned(matching_attributes
["vm_object"])):
3473 matched_attributes
+= 1
3474 if "vmp_offset" in matching_attributes
and (unsigned(page
.vmp_offset
) == unsigned(matching_attributes
["vmp_offset"])):
3475 matched_attributes
+= 1
3476 if "phys_page" in matching_attributes
and (unsigned(_vm_page_get_phys_page(page_ptr
)) == unsigned(matching_attributes
["phys_page"])):
3477 matched_attributes
+= 1
3478 if "bitfield" in matching_attributes
and unsigned(page
.__getattr
__(matching_attributes
["bitfield"])) == 1:
3479 matched_attributes
+= 1
3481 return matched_attributes
3483 #Macro scan_vm_pages
3484 @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"))
3485 @lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
3486 def ScanVMPages(cmd_args
=None, cmd_options
={}):
3487 """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
3488 usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
3490 scan_vm_pages -A: scan vm pages in the global vm_pages array
3491 scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
3492 scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
3493 scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
3494 scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
3495 scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
3496 scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
3497 scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
3498 scan_vm_pages <-A> -N <npages>: Scan at most npages
3500 if (len(cmd_options
) < 1):
3501 raise ArgumentError("Please specify at least one matching attribute")
3503 vm_pages
= kern
.globals.vm_pages
3504 vm_pages_count
= kern
.globals.vm_pages_count
3507 npages
= vm_pages_count
3508 scan_vmpages_array
= False
3509 scan_vmpages_zone
= False
3512 if "-A" in cmd_options
:
3513 scan_vmpages_array
= True
3515 if "-Z" in cmd_options
:
3516 scan_vmpages_zone
= True
3518 if scan_vmpages_array
== False and scan_vmpages_zone
== False:
3519 raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
3521 attribute_values
= {}
3522 if "-S" in cmd_options
:
3523 attribute_values
["vmp_q_state"] = kern
.GetValueFromAddress(cmd_options
["-S"], 'int')
3524 attribute_count
+= 1
3526 if "-O" in cmd_options
:
3527 attribute_values
["vm_object"] = kern
.GetValueFromAddress(cmd_options
["-O"], 'vm_object_t')
3528 attribute_count
+= 1
3530 if "-F" in cmd_options
:
3531 attribute_values
["vmp_offset"] = kern
.GetValueFromAddress(cmd_options
["-F"], 'unsigned long long')
3532 attribute_count
+= 1
3534 if "-P" in cmd_options
:
3535 attribute_values
["phys_page"] = kern
.GetValueFromAddress(cmd_options
["-P"], 'unsigned int')
3536 attribute_count
+= 1
3538 if "-B" in cmd_options
:
3539 valid_vmp_bitfields
= [
3540 "vmp_in_background",
3541 "vmp_on_backgroundq",
3555 "vmp_free_when_done",
3569 "vmp_written_by_kernel",
3570 "vmp_unused_object_bits"
3572 attribute_values
["bitfield"] = cmd_options
["-B"]
3573 if attribute_values
["bitfield"] in valid_vmp_bitfields
:
3574 attribute_count
+= 1
3576 raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield
))
3578 if "-I" in cmd_options
:
3579 start_index
= kern
.GetValueFromAddress(cmd_options
["-I"], 'int')
3580 npages
= vm_pages_count
- start_index
3582 if "-N" in cmd_options
:
3583 npages
= kern
.GetValueFromAddress(cmd_options
["-N"], 'int')
3585 raise ArgumentError("You specified -N 0, nothing to be scanned")
3587 end_index
= start_index
+ npages
- 1
3588 if end_index
>= vm_pages_count
:
3589 raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count
))
3591 header_after_n_lines
= 40
3592 format_string
= "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
3595 if scan_vmpages_array
:
3596 print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index
, end_index
, attribute_count
)
3598 while i
<= end_index
:
3600 if match_vm_page_attributes(page
, attribute_values
) == attribute_count
:
3601 if found_in_array
% header_after_n_lines
== 0:
3602 print ScanVMPages
.header
3604 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
)))
3610 if scan_vmpages_zone
:
3611 page_size
= kern
.GetGlobalVariable('page_size')
3612 num_zones
= kern
.GetGlobalVariable('num_zones')
3613 zone_array
= kern
.GetGlobalVariable('zone_array')
3614 print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count
)
3616 while i
< num_zones
:
3617 zone
= zone_array
[i
]
3618 if str(zone
.zone_name
) == "vm pages":
3623 print "Cannot find vm_pages zone, skip the scan"
3625 print "Scanning page queues in the vm_pages zone..."
3626 elements
= FindAllocatedElementsInZone(zone
)
3627 for elem
in elements
:
3628 page
= kern
.GetValueFromAddress(elem
, 'vm_page_t')
3630 if match_vm_page_attributes(page
, attribute_values
) == attribute_count
:
3631 if found_in_zone
% header_after_n_lines
== 0:
3632 print ScanVMPages
.header
3634 vm_object
= _vm_page_unpack_ptr(page
.vmp_object
)
3635 phys_page
= _vm_page_get_phys_page(page
)
3636 print format_string
.format("vm_pages zone", elem
, page
.vmp_q_state
, vm_object
, page
.vmp_offset
, phys_page
)
3639 total
= found_in_array
+ found_in_zone
3640 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
)
3642 #EndMacro scan_vm_pages
3644 VM_PAGE_IS_WIRED
= 1
3646 @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"))
3647 @lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
3648 def VMObjectWalkPages(cmd_args
=None, cmd_options
={}):
3649 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
3650 specifically look for this page, highlighting it in the output or noting if it was not found. For
3651 each page, we confirm that it points to the object. We also keep track of the number of pages we
3652 see and compare this to the object's resident page count field.
3654 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
3655 vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
3656 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
3657 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
3658 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)
3659 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
3660 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
3661 vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
3665 if (cmd_args
== None or len(cmd_args
) < 1):
3666 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
3670 obj
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
3673 if "-P" in cmd_options
:
3674 page
= kern
.GetValueFromAddress(cmd_options
['-P'], 'vm_page_t')
3677 if "-O" in cmd_options
:
3678 off
= kern
.GetValueFromAddress(cmd_options
['-O'], 'vm_offset_t')
3681 if "-S" in cmd_options
:
3682 if page
== 0 and off
< 0:
3683 raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
3686 walk_backwards
= False
3687 if "-B" in cmd_options
:
3688 walk_backwards
= True
3691 if "-Q" in cmd_options
:
3695 print VMObjectWalkPages
.header
3696 format_string
= "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
3697 first_bitfield_format_string
= "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
3698 second_bitfield_format_string
= "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
3699 second_bitfield_format_string
+= "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
3700 second_bitfield_format_string
+= "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
3701 second_bitfield_format_string
+= "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
3703 limit
= 4096 #arbitrary limit of number of pages to walk
3705 if "-N" in cmd_options
:
3709 if "-C" in cmd_options
:
3713 res_page_count
= unsigned(obj
.resident_page_count
)
3717 for vmp
in IterateQueue(obj
.memq
, "vm_page_t", "vmp_listq", walk_backwards
, unpack_ptr_fn
=_vm_page_unpack_ptr
):
3720 if (page
!= 0 and not(page_found
) and vmp
== page
):
3721 out_string
+= "******"
3724 if (off
> 0 and not(page_found
) and vmp
.vmp_offset
== off
):
3725 out_string
+= "******"
3728 if page
!= 0 or off
> 0 or quiet_mode
:
3729 if (page_count
% 1000) == 0:
3730 print "traversed %d pages ...\n" % (page_count
)
3732 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
)
3733 out_string
+= first_bitfield_format_string
.format(vmp
.vmp_q_state
, vmp
.vmp_in_background
, vmp
.vmp_on_backgroundq
, vmp
.vmp_gobbled
, vmp
.vmp_laundry
, vmp
.vmp_no_cache
,
3734 vmp
.vmp_private
, vmp
.vmp_reference
)
3736 if hasattr(vmp
,'slid'):
3740 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
,
3741 vmp
.vmp_pmapped
, vmp
.vmp_xpmapped
, vmp
.vmp_wpmapped
, vmp
.vmp_free_when_done
, vmp
.vmp_absent
,
3742 vmp
.vmp_error
, vmp
.vmp_dirty
, vmp
.vmp_cleaning
, vmp
.vmp_precious
, vmp
.vmp_overwriting
,
3743 vmp
.vmp_restart
, vmp
.vmp_unusual
, 0, 0,
3744 vmp
.vmp_cs_validated
, vmp
.vmp_cs_tainted
, vmp
.vmp_cs_nx
, vmp
.vmp_reusable
, vmp
.vmp_lopage
, vmp_slid
,
3745 vmp
.vmp_written_by_kernel
)
3747 if (vmp
in pages_seen
):
3748 print out_string
+ "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " twice. stopping...\n"
3751 if (_vm_page_unpack_ptr(vmp
.vmp_object
) != unsigned(obj
)):
3752 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
)))
3755 if (vmp
.vmp_q_state
== VM_PAGE_IS_WIRED
) and (vmp
.vmp_wire_count
== 0):
3756 print out_string
+ " page in wired state with wire_count of 0\n"
3757 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + "\n"
3758 print "stopping...\n"
3761 if ((vmp
.vmp_unused_page_bits
!= 0) or (vmp
.vmp_unused_object_bits
!= 0)):
3762 print out_string
+ " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " unused__pageq_bits: %d unused_object_bits : %d\n" % (vmp
.vmp_unused_page_bits
,
3763 vmp
.vmp_unused_object_bits
)
3764 print "stopping...\n"
3770 hash_id
= _calc_vm_page_hash(obj
, vmp
.vmp_offset
)
3771 hash_page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3772 hash_page
= _vm_page_unpack_ptr(hash_page_list
)
3775 while (hash_page
!= 0):
3776 hash_page_t
= kern
.GetValueFromAddress(hash_page
, 'vm_page_t')
3777 if hash_page_t
== vmp
:
3779 hash_page
= _vm_page_unpack_ptr(hash_page_t
.vmp_next_m
)
3781 if (unsigned(vmp
) != unsigned(hash_page_t
)):
3782 print out_string
+ "unable to find page: " + "{0: <#020x}".format(unsigned(vmp
)) + " from object in kernel page bucket list\n"
3783 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args
[0], unsigned(vmp
.vmp_offset
)))
3786 if (page_count
>= limit
and not(ignore_limit
)):
3787 print out_string
+ "Limit reached (%d pages), stopping..." % (limit
)
3792 if page_found
and stop
:
3793 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
)))
3797 print("page found? : %s\n" % page_found
)
3800 print("page found? : %s\n" % page_found
)
3802 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
)))
3804 if show_compressed
!= 0 and obj
.pager
!= 0 and unsigned(obj
.pager
.mo_pager_ops
) == unsigned(addressof(kern
.globals.compressor_pager_ops
)):
3805 pager
= Cast(obj
.pager
, 'compressor_pager *')
3806 chunks
= pager
.cpgr_num_slots
/ 128
3807 pagesize
= kern
.globals.page_size
3810 while page_idx
< pager
.cpgr_num_slots
:
3812 chunk
= pager
.cpgr_slots
.cpgr_islots
[page_idx
/ 128]
3813 slot
= chunk
[page_idx
% 128]
3814 elif pager
.cpgr_num_slots
> 2:
3815 slot
= pager
.cpgr_slots
.cpgr_dslots
[page_idx
]
3817 slot
= pager
.cpgr_slots
.cpgr_eslots
[page_idx
]
3820 print("compressed page for offset: %x slot %x\n" % ((page_idx
* pagesize
) - obj
.paging_offset
, slot
))
3821 page_idx
= page_idx
+ 1
3824 @lldb_command("show_all_apple_protect_pagers")
3825 def ShowAllAppleProtectPagers(cmd_args
=None):
3826 """Routine to print all apple_protect pagers
3827 usage: show_all_apple_protect_pagers
3829 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
3830 qhead
= kern
.globals.apple_protect_pager_queue
3831 qtype
= GetType('apple_protect_pager *')
3832 qcnt
= kern
.globals.apple_protect_pager_count
3834 for pager
in IterateQueue(qhead
, qtype
, "pager_queue"):
3836 show_apple_protect_pager(pager
, qcnt
, idx
)
3838 @lldb_command("show_apple_protect_pager")
3839 def ShowAppleProtectPager(cmd_args
=None):
3840 """Routine to print out info about an apple_protect pager
3841 usage: show_apple_protect_pager <pager>
3843 if cmd_args
== None or len(cmd_args
) < 1:
3844 print "Invalid argument.", ShowAppleProtectPager
.__doc
__
3846 pager
= kern
.GetValueFromAddress(cmd_args
[0], 'apple_protect_pager_t')
3847 show_apple_protect_pager(pager
, 1, 1)
3849 def show_apple_protect_pager(pager
, qcnt
, idx
):
3850 object = pager
.backing_object
3851 shadow
= object.shadow
3854 shadow
= object.shadow
3855 vnode_pager
= Cast(object.pager
,'vnode_pager *')
3856 filename
= GetVnodePath(vnode_pager
.vnode_handle
)
3857 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {: <#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
, pager
.ref_count
, pager
.is_ready
, pager
.is_mapped
, pager
.pager_control
, 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
)
3859 @lldb_command("show_console_ring")
3860 def ShowConsoleRingData(cmd_args
=None):
3861 """ Print console ring buffer stats and data
3863 cr
= kern
.globals.console_ring
3864 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
)
3866 for i
in range(unsigned(cr
.used
)):
3867 idx
= ((unsigned(cr
.read_ptr
) - unsigned(cr
.buffer)) + i
) % unsigned(cr
.len)
3868 pending_data
.append("{:c}".format(cr
.buffer[idx
]))
3872 print "".join(pending_data
)
3874 # Macro: showjetsamsnapshot
3876 @lldb_command("showjetsamsnapshot", "DA")
3877 def ShowJetsamSnapshot(cmd_args
=None, cmd_options
={}):
3878 """ Dump entries in the jetsam snapshot table
3879 usage: showjetsamsnapshot [-D] [-A]
3880 Use -D flag to print extra physfootprint details
3881 Use -A flag to print all entries (regardless of valid count)
3884 # Not shown are uuid, user_data, cpu_time
3888 show_footprint_details
= False
3889 show_all_entries
= False
3891 if "-D" in cmd_options
:
3892 show_footprint_details
= True
3894 if "-A" in cmd_options
:
3895 show_all_entries
= True
3897 valid_count
= kern
.globals.memorystatus_jetsam_snapshot_count
3898 max_count
= kern
.globals.memorystatus_jetsam_snapshot_max
3900 if (show_all_entries
== True):
3905 print "{:s}".format(valid_count
)
3906 print "{:s}".format(max_count
)
3909 print "The jetsam snapshot is empty."
3910 print "Use -A to force dump all entries (regardless of valid count)"
3913 # Dumps the snapshot header info
3914 print lldb_run_command('p *memorystatus_jetsam_snapshot')
3916 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}"
3917 if (show_footprint_details
== True):
3918 hdr_format
+= "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
3921 if (show_footprint_details
== False):
3922 print hdr_format
.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
3923 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
3925 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')
3926 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
3929 entry_format
= "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
3930 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
3931 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
3932 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
3933 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
3935 if (show_footprint_details
== True):
3936 entry_format
+= "{e.jse_internal_pages: >15d} "\
3937 "{e.jse_internal_compressed_pages: >15d} "\
3938 "{e.jse_iokit_mapped_pages: >12d} "\
3939 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
3940 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
3941 "{e.jse_alternate_accounting_pages: >10d} "\
3942 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
3943 "{e.jse_page_table_pages: >10d}"
3945 snapshot_list
= kern
.globals.memorystatus_jetsam_snapshot
.entries
3948 current_entry
= dereference(Cast(addressof(snapshot_list
[idx
]), 'jetsam_snapshot_entry *'))
3949 print entry_format
.format(index
=idx
, e
=current_entry
)
3953 # EndMacro: showjetsamsnapshot
3955 # Macro: showvnodecleanblk/showvnodedirtyblk
3957 def _GetBufSummary(buf
):
3958 """ Get a summary of important information out of a buf_t.
3960 initial
= "(struct buf) {0: <#0x} ="
3962 # List all of the fields in this buf summary.
3963 entries
= [buf
.b_hash
, buf
.b_vnbufs
, buf
.b_freelist
, buf
.b_timestamp
, buf
.b_whichq
,
3964 buf
.b_flags
, buf
.b_lflags
, buf
.b_error
, buf
.b_bufsize
, buf
.b_bcount
, buf
.b_resid
,
3965 buf
.b_dev
, buf
.b_datap
, buf
.b_lblkno
, buf
.b_blkno
, buf
.b_iodone
, buf
.b_vp
,
3966 buf
.b_rcred
, buf
.b_wcred
, buf
.b_upl
, buf
.b_real_bp
, buf
.b_act
, buf
.b_drvdata
,
3967 buf
.b_fsprivate
, buf
.b_transaction
, buf
.b_dirtyoff
, buf
.b_dirtyend
, buf
.b_validoff
,
3968 buf
.b_validend
, buf
.b_redundancy_flags
, buf
.b_proc
, buf
.b_attr
]
3970 # Join an (already decent) string representation of each field
3971 # with newlines and indent the region.
3972 joined_strs
= "\n".join([str(i
).rstrip() for i
in entries
]).replace('\n', "\n ")
3974 # Add the total string representation to our title and return it.
3975 out_str
= initial
.format(int(buf
)) + " {\n " + joined_strs + "\n}\n\n"
3978 def _ShowVnodeBlocks(dirty
=True, cmd_args
=None):
3979 """ Display info about all [dirty|clean] blocks in a vnode.
3981 if cmd_args
== None or len(cmd_args
) < 1:
3982 print "Please provide a valid vnode argument."
3985 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
3986 list_head
= vnodeval
.v_cleanblkhd
;
3988 list_head
= vnodeval
.v_dirtyblkhd
3990 print "Blocklist for vnode {}:".format(cmd_args
[0])
3993 for buf
in IterateListEntry(list_head
, 'struct buf *', 'b_hash'):
3994 # For each block (buf_t) in the appropriate list,
3995 # ask for a summary and print it.
3996 print "---->\nblock {}: ".format(i
) + _GetBufSummary(buf
)
4000 @lldb_command('showvnodecleanblk')
4001 def ShowVnodeCleanBlocks(cmd_args
=None):
4002 """ Display info about all clean blocks in a vnode.
4003 usage: showvnodecleanblk <address of vnode>
4005 _ShowVnodeBlocks(False, cmd_args
)
4007 @lldb_command('showvnodedirtyblk')
4008 def ShowVnodeDirtyBlocks(cmd_args
=None):
4009 """ Display info about all dirty blocks in a vnode.
4010 usage: showvnodedirtyblk <address of vnode>
4012 _ShowVnodeBlocks(True, cmd_args
)
4014 # EndMacro: showvnodecleanblk/showvnodedirtyblk
4017 @lldb_command("vm_page_lookup_in_map")
4018 def VmPageLookupInMap(cmd_args
=None):
4019 """Lookup up a page at a virtual address in a VM map
4020 usage: vm_page_lookup_in_map <map> <vaddr>
4022 if cmd_args
== None or len(cmd_args
) < 2:
4023 print "Invalid argument.", VmPageLookupInMap
.__doc
__
4025 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
4026 vaddr
= kern
.GetValueFromAddress(cmd_args
[1], 'vm_map_offset_t')
4027 print "vaddr {:#018x} in map {: <#018x}".format(vaddr
, map)
4028 vm_page_lookup_in_map(map, vaddr
)
4030 def vm_page_lookup_in_map(map, vaddr
):
4031 vaddr
= unsigned(vaddr
)
4032 vme_list_head
= map.hdr
.links
4033 vme_ptr_type
= GetType('vm_map_entry *')
4034 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
4035 if unsigned(vme
.links
.start
) > vaddr
:
4037 if unsigned(vme
.links
.end
) <= vaddr
:
4039 offset_in_vme
= vaddr
- unsigned(vme
.links
.start
)
4040 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
), vme
.vme_object
.vmo_object
, unsigned(vme
.vme_offset
) & ~
0xFFF)
4041 offset_in_object
= offset_in_vme
+ (unsigned(vme
.vme_offset
) & ~
0xFFF)
4043 print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object
, vme
.vme_object
.vmo_submap
)
4044 vm_page_lookup_in_map(vme
.vme_object
.vmo_submap
, offset_in_object
)
4046 vm_page_lookup_in_object(vme
.vme_object
.vmo_object
, offset_in_object
)
4048 @lldb_command("vm_page_lookup_in_object")
4049 def VmPageLookupInObject(cmd_args
=None):
4050 """Lookup up a page at a given offset in a VM object
4051 usage: vm_page_lookup_in_object <object> <offset>
4053 if cmd_args
== None or len(cmd_args
) < 2:
4054 print "Invalid argument.", VmPageLookupInObject
.__doc
__
4056 object = kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
4057 offset
= kern
.GetValueFromAddress(cmd_args
[1], 'vm_object_offset_t')
4058 print "offset {:#018x} in object {: <#018x}".format(offset
, object)
4059 vm_page_lookup_in_object(object, offset
)
4061 def vm_page_lookup_in_object(object, offset
):
4062 offset
= unsigned(offset
)
4063 page_size
= kern
.globals.page_size
4064 trunc_offset
= offset
& ~
(page_size
- 1)
4065 print " offset {:#018x} in VM object {: <#018x}".format(offset
, object)
4066 hash_id
= _calc_vm_page_hash(object, trunc_offset
)
4067 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
4068 page
= _vm_page_unpack_ptr(page_list
)
4070 m
= kern
.GetValueFromAddress(page
, 'vm_page_t')
4071 m_object_val
= _vm_page_unpack_ptr(m
.vmp_object
)
4072 m_object
= kern
.GetValueFromAddress(m_object_val
, 'vm_object_t')
4073 if unsigned(m_object
) != unsigned(object) or unsigned(m
.vmp_offset
) != unsigned(trunc_offset
):
4074 page
= _vm_page_unpack_ptr(m
.vmp_next_m
)
4076 print " resident page {: <#018x} phys {:#010x}".format(m
, _vm_page_get_phys_page(m
))
4078 if object.pager
and object.pager_ready
:
4079 offset_in_pager
= trunc_offset
+ unsigned(object.paging_offset
)
4080 if not object.internal
:
4081 print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager
, object.pager
.mo_pager_ops
.memory_object_pager_name
, object.pager
)
4083 pager
= Cast(object.pager
, 'compressor_pager *')
4084 ret
= vm_page_lookup_in_compressor_pager(pager
, offset_in_pager
)
4087 if object.shadow
and not object.phys_contiguous
:
4088 offset_in_shadow
= offset
+ unsigned(object.vo_un2
.vou_shadow_offset
)
4089 vm_page_lookup_in_object(object.shadow
, offset_in_shadow
)
4091 print " page is absent and will be zero-filled on demand"
4094 @lldb_command("vm_page_lookup_in_compressor_pager")
4095 def VmPageLookupInCompressorPager(cmd_args
=None):
4096 """Lookup up a page at a given offset in a compressor pager
4097 usage: vm_page_lookup_in_compressor_pager <pager> <offset>
4099 if cmd_args
== None or len(cmd_args
) < 2:
4100 print "Invalid argument.", VmPageLookupInCompressorPager
.__doc
__
4102 pager
= kern
.GetValueFromAddress(cmd_args
[0], 'compressor_pager_t')
4103 offset
= kern
.GetValueFromAddress(cmd_args
[1], 'memory_object_offset_t')
4104 print "offset {:#018x} in compressor pager {: <#018x}".format(offset
, pager
)
4105 vm_page_lookup_in_compressor_pager(pager
, offset
)
4107 def vm_page_lookup_in_compressor_pager(pager
, offset
):
4108 offset
= unsigned(offset
)
4109 page_size
= unsigned(kern
.globals.page_size
)
4110 page_num
= unsigned(offset
/ page_size
)
4111 if page_num
> pager
.cpgr_num_slots
:
4112 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
)
4114 slots_per_chunk
= 512 / sizeof ('compressor_slot_t')
4115 num_chunks
= unsigned((pager
.cpgr_num_slots
+slots_per_chunk
-1) / slots_per_chunk
)
4117 chunk_idx
= unsigned(page_num
/ slots_per_chunk
)
4118 chunk
= pager
.cpgr_slots
.cpgr_islots
[chunk_idx
]
4119 slot_idx
= unsigned(page_num
% slots_per_chunk
)
4120 slot
= GetObjectAtIndexFromArray(chunk
, slot_idx
)
4121 slot_str
= "islots[{:d}][{:d}]".format(chunk_idx
, slot_idx
)
4122 elif pager
.cpgr_num_slots
> 2:
4124 slot
= GetObjectAtIndexFromArray(pager
.cpgr_slots
.cpgr_dslots
, slot_idx
)
4125 slot_str
= "dslots[{:d}]".format(slot_idx
)
4128 slot
= GetObjectAtIndexFromArray(pager
.cpgr_slots
.cpgr_eslots
, slot_idx
)
4129 slot_str
= "eslots[{:d}]".format(slot_idx
)
4130 print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset
, pager
, slot_str
, slot
)
4133 slot_value
= dereference(slot
)
4134 print " value {:#010x}".format(slot_value
)
4135 vm_page_lookup_in_compressor(Cast(slot
, 'c_slot_mapping_t'))
4138 @lldb_command("vm_page_lookup_in_compressor")
4139 def VmPageLookupInCompressor(cmd_args
=None):
4140 """Lookup up a page in a given compressor slot
4141 usage: vm_page_lookup_in_compressor <slot>
4143 if cmd_args
== None or len(cmd_args
) < 1:
4144 print "Invalid argument.", VmPageLookupInCompressor
.__doc
__
4146 slot
= kern
.GetValueFromAddress(cmd_args
[0], 'compressor_slot_t *')
4147 print "compressor slot {: <#018x}".format(slot
)
4148 vm_page_lookup_in_compressor(slot
)
4150 C_SV_CSEG_ID
= ((1 << 22) - 1)
4152 def vm_page_lookup_in_compressor(slot_ptr
):
4153 slot_ptr
= Cast(slot_ptr
, 'compressor_slot_t *')
4154 slot_value
= dereference(slot_ptr
)
4155 slot
= Cast(slot_value
, 'c_slot_mapping')
4157 print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr
), unsigned(slot_value
), slot
.s_cseg
, slot
.s_cindx
)
4160 if slot
.s_cseg
== C_SV_CSEG_ID
:
4161 sv
= kern
.globals.c_segment_sv_hash_table
4162 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
)
4164 if slot
.s_cseg
== 0 or unsigned(slot
.s_cseg
) > unsigned(kern
.globals.c_segments_available
):
4165 print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot
.s_cseg
, unsigned(kern
.globals.c_segments_available
))
4167 c_segments
= kern
.globals.c_segments
4168 c_segments_elt
= GetObjectAtIndexFromArray(c_segments
, slot
.s_cseg
-1)
4169 c_seg
= c_segments_elt
.c_seg
4171 if hasattr(c_seg
, 'c_state'):
4172 c_state
= c_seg
.c_state
4174 c_state_str
= "C_IS_EMPTY"
4177 c_state_str
= "C_IS_FREE"
4180 c_state_str
= "C_IS_FILLING"
4182 c_state_str
= "C_ON_AGE_Q"
4184 c_state_str
= "C_ON_SWAPOUT_Q"
4186 c_state_str
= "C_ON_SWAPPEDOUT_Q"
4189 c_state_str
= "C_ON_SWAPPEDOUTSPARSE_Q"
4192 c_state_str
= "C_ON_SWAPPEDIN_Q"
4194 c_state_str
= "C_ON_MAJORCOMPACT_Q"
4196 c_state_str
= "C_ON_BAD_Q"
4199 c_state_str
= "<unknown>"
4202 c_state_str
= "<no c_state field>"
4203 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
)
4204 c_indx
= unsigned(slot
.s_cindx
)
4205 if hasattr(c_seg
, 'c_slot_var_array'):
4206 c_seg_fixed_array_len
= kern
.globals.c_seg_fixed_array_len
4207 if c_indx
< c_seg_fixed_array_len
:
4208 cs
= c_seg
.c_slot_fixed_array
[c_indx
]
4210 cs
= GetObjectAtIndexFromArray(c_seg
.c_slot_var_array
, c_indx
- c_seg_fixed_array_len
)
4212 C_SEG_SLOT_ARRAY_SIZE
= 64
4213 C_SEG_SLOT_ARRAY_MASK
= C_SEG_SLOT_ARRAY_SIZE
- 1
4214 cs
= GetObjectAtIndexFromArray(c_seg
.c_slots
[c_indx
/ C_SEG_SLOT_ARRAY_SIZE
], c_indx
& C_SEG_SLOT_ARRAY_MASK
)
4216 c_slot_unpacked_ptr
= (unsigned(cs
.c_packed_ptr
) << 2) + vm_min_kernel_and_kext_address()
4217 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
))
4218 if unsigned(slot_ptr
) != unsigned(c_slot_unpacked_ptr
):
4219 print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr
, c_slot_unpacked_ptr
)
4221 c_data
= c_seg
.c_store
.c_buffer
+ (4 * cs
.c_offset
)
4223 cmd
= "memory read {: <#018x} {: <#018x} --force".format(c_data
, c_data
+ c_size
)
4225 print lldb_run_command(cmd
)
4227 print "<no compressed data>"
4229 def vm_min_kernel_and_kext_address(cmd_args
=None):
4230 if hasattr(kern
.globals, 'vm_min_kernel_and_kext_address'):
4231 return unsigned(kern
.globals.vm_min_kernel_and_kext_address
)
4232 elif kern
.arch
== 'x86_64':
4233 return unsigned(0xffffff7f80000000)
4234 elif kern
.arch
== 'arm64':
4235 return unsigned(0xffffff8000000000)
4236 elif kern
.arch
== 'arm':
4237 return unsigned(0x80000000)
4239 print "vm_min_kernel_and_kext_address(): unknown arch '{:s}'".format(kern
.arch
)
4242 def print_hex_data(data
, begin_offset
=0, desc
=""):
4243 """ print on stdout "hexdump -C < data" like output
4245 data - bytearray or array of int where each int < 255
4246 begin_offset - int offset that should be printed in left column
4247 desc - str optional description to print on the first line to describe data
4250 print "{}:".format(desc
)
4252 total_len
= len(data
)
4255 while index
< total_len
:
4256 hex_buf
+= " {:02x}".format(data
[index
])
4257 if data
[index
] < 0x20 or data
[index
] > 0x7e:
4260 char_buf
+= "{:c}".format(data
[index
])
4262 if index
and index
% 8 == 0:
4264 if index
> 1 and (index
% 16) == 0:
4265 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset
+ index
- 16, hex_buf
, char_buf
)
4268 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset
+ index
- 16, hex_buf
, char_buf
)
4271 @lldb_command('vm_scan_all_pages')
4272 def VMScanAllPages(cmd_args
=None):
4273 """Scans the vm_pages[] array
4275 vm_pages_count
= kern
.globals.vm_pages_count
4276 vm_pages
= kern
.globals.vm_pages
4279 local_free_count
= 0
4281 local_active_count
= 0
4283 speculative_count
= 0
4286 compressor_count
= 0
4287 pageable_internal_count
= 0
4288 pageable_external_count
= 0
4290 secluded_free_count
= 0
4291 secluded_inuse_count
= 0
4294 while i
< vm_pages_count
:
4297 print "{:d}/{:d}...\n".format(i
,vm_pages_count
)
4303 m_object_val
= _vm_page_unpack_ptr(m
.vmp_object
)
4306 if m_object
.internal
:
4311 if m
.vmp_wire_count
!= 0 and m
.vmp_local
== 0:
4312 wired_count
= wired_count
+ 1
4314 elif m
.vmp_throttled
:
4315 throttled_count
= throttled_count
+ 1
4318 active_count
= active_count
+ 1
4321 local_active_count
= local_active_count
+ 1
4323 elif m
.vmp_inactive
:
4324 inactive_count
= inactive_count
+ 1
4326 elif m
.vmp_speculative
:
4327 speculative_count
= speculative_count
+ 1
4330 free_count
= free_count
+ 1
4332 elif m
.vmp_secluded
:
4333 secluded_count
= secluded_count
+ 1
4335 secluded_free_count
= secluded_free_count
+ 1
4337 secluded_inuse_count
= secluded_inuse_count
+ 1
4339 elif m_object
== 0 and m
.vmp_busy
:
4340 local_free_count
= local_free_count
+ 1
4342 elif m
.vmp_compressor
:
4343 compressor_count
= compressor_count
+ 1
4346 print "weird page vm_pages[{:d}]?\n".format(i
)
4351 pageable_internal_count
= pageable_internal_count
+ 1
4353 pageable_external_count
= pageable_external_count
+ 1
4356 print "vm_pages_count = {:d}\n".format(vm_pages_count
)
4358 print "wired_count = {:d}\n".format(wired_count
)
4359 print "throttled_count = {:d}\n".format(throttled_count
)
4360 print "active_count = {:d}\n".format(active_count
)
4361 print "local_active_count = {:d}\n".format(local_active_count
)
4362 print "inactive_count = {:d}\n".format(inactive_count
)
4363 print "speculative_count = {:d}\n".format(speculative_count
)
4364 print "free_count = {:d}\n".format(free_count
)
4365 print "local_free_count = {:d}\n".format(local_free_count
)
4366 print "compressor_count = {:d}\n".format(compressor_count
)
4368 print "pageable_internal_count = {:d}\n".format(pageable_internal_count
)
4369 print "pageable_external_count = {:d}\n".format(pageable_external_count
)
4370 print "secluded_count = {:d}\n".format(secluded_count
)
4371 print "secluded_free_count = {:d}\n".format(secluded_free_count
)
4372 print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count
)
4375 @lldb_command('show_all_vm_named_entries')
4376 def ShowAllVMNamedEntries(cmd_args
=None):
4377 """ Routine to print a summary listing of all the VM named entries
4379 queue_len
= kern
.globals.vm_named_entry_count
4380 queue_head
= kern
.globals.vm_named_entry_list
4382 print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern
.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len
)
4384 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")
4386 for entry
in IterateQueue(queue_head
, 'struct vm_named_entry *', 'named_entry_list'):
4388 showmemoryentry(entry
, idx
, queue_len
)
4390 @lldb_command('show_vm_named_entry')
4391 def ShowVMNamedEntry(cmd_args
=None):
4392 """ Routine to print a VM named entry
4394 if cmd_args
== None or len(cmd_args
) < 1:
4395 print "Invalid argument.", ShowMapVMNamedEntry
.__doc
__
4397 named_entry
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_named_entry_t')
4398 showmemoryentry(named_entry
, 0, 0)
4400 def showmemoryentry(entry
, idx
=0, queue_len
=0):
4401 """ Routine to print out a summary a VM memory entry
4403 entry - core.value : a object of type 'struct vm_named_entry *'
4407 show_pager_info
= True
4408 show_all_shadows
= True
4411 if entry
.is_sub_map
== 1:
4413 if entry
.is_copy
== 1:
4415 if entry
.is_sub_map
== 0 and entry
.is_copy
== 0:
4418 if entry
.protection
& 0x1:
4422 if entry
.protection
& 0x2:
4426 if entry
.protection
& 0x4:
4431 if hasattr(entry
, 'named_entry_alias'):
4432 extra_str
+= " alias={:d}".format(entry
.named_entry_alias
)
4433 if hasattr(entry
, 'named_entry_port'):
4434 extra_str
+= " port={:#016x}".format(entry
.named_entry_port
)
4435 print "{:>6d}/{:<6d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx
,queue_len
,entry
,entry
.ref_count
,entry
.protection
,prot
,backing
,entry
.backing
.object,entry
.offset
,entry
.data_offset
,entry
.size
,extra_str
)
4436 if entry
.is_sub_map
== 1:
4437 showmapvme(entry
.backing
.map, 0, 0, show_pager_info
, show_all_shadows
)
4438 if entry
.is_copy
== 1:
4439 showmapcopyvme(entry
.backing
.copy
, 0, 0, show_pager_info
, show_all_shadows
, 0)
4440 if entry
.is_sub_map
== 0 and entry
.is_copy
== 0:
4441 showvmobject(entry
.backing
.object, entry
.offset
, entry
.size
, show_pager_info
, show_all_shadows
)
4444 def IterateRBTreeEntry2(element
, element_type
, field_name1
, field_name2
):
4445 """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
4446 element - value : Value object for rbh_root
4447 element_type - str : Type of the link element
4448 field_name - str : Name of the field in link element's structure
4450 A generator does not return. It is used for iterating
4451 value : an object thats of type (element_type) head->sle_next. Always a pointer object
4453 elt
= element
.__getattr
__('rbh_root')
4454 if type(element_type
) == str:
4455 element_type
= gettype(element_type
)
4456 charp_type
= gettype('char *');
4460 while unsigned(elt
) != 0:
4462 elt
= cast(elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4466 while unsigned(elt
) != 0:
4468 # implementation cribbed from RB_NEXT in libkern/tree.h
4469 right
= cast(elt
.__getattr
__(field_name1
).__getattr
__(fieldname2
).__getattr
__('rbe_right'), element_type
)
4470 if unsigned(right
) != 0:
4472 left
= cast(elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4473 while unsigned(left
) != 0:
4475 left
= cast(elt
.__getattr
__(field_name1
).__getattr
(__field_name2
).__getattr
__('rbe_left'), element_type
)
4478 # avoid using GetValueFromAddress
4479 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4480 parent
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4481 parent
= cast(parent
, element_type
)
4483 if unsigned(parent
) != 0:
4484 left
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4485 if (unsigned(parent
) != 0) and (unsigned(elt
) == unsigned(left
)):
4488 if unsigned(parent
) != 0:
4489 right
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_right'), element_type
)
4490 while unsigned(parent
) != 0 and (unsigned(elt
) == unsigned(right
)):
4493 # avoid using GetValueFromAddress
4494 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4495 parent
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4496 parent
= cast(parent
, element_type
)
4498 right
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_right'), element_type
)
4500 # avoid using GetValueFromAddress
4501 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4502 elt
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4503 elt
= cast(elt
, element_type
)
4506 @lldb_command("showmaprb")
4507 def ShowMapRB(cmd_args
=None):
4508 """Routine to print out a VM map's RB tree
4509 usage: showmaprb <vm_map>
4511 if cmd_args
== None or len(cmd_args
) < 1:
4512 print "Invalid argument.", ShowMapRB
.__doc
__
4514 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
4515 print GetVMMapSummary
.header
4516 print GetVMMapSummary(map_val
)
4517 vme_rb_root
= map_val
.hdr
.rb_head_store
4518 vme_ptr_type
= GetType('struct vm_map_entry *')
4519 print GetVMEntrySummary
.header
4520 for vme
in IterateRBTreeEntry2(vme_rb_root
, 'struct vm_map_entry *', 'store', 'entry'):
4521 print GetVMEntrySummary(vme
)
4524 @lldb_command('show_all_owned_objects', 'T')
4525 def ShowAllOwnedObjects(cmd_args
=None, cmd_options
={}):
4526 """ Routine to print the list of VM objects owned by each task
4527 -T: show only ledger-tagged objects
4529 showonlytagged
= False
4530 if "-T" in cmd_options
:
4531 showonlytagged
= True
4532 for task
in kern
.tasks
:
4533 ShowTaskOwnedVmObjects(task
, showonlytagged
)
4535 @lldb_command('show_task_owned_objects', 'T')
4536 def ShowTaskOwnedObjects(cmd_args
=None, cmd_options
={}):
4537 """ Routine to print the list of VM objects owned by the specified task
4538 -T: show only ledger-tagged objects
4540 showonlytagged
= False
4541 if "-T" in cmd_options
:
4542 showonlytagged
= True
4543 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
4544 ShowTaskOwnedVmObjects(task
, showonlytagged
)
4546 def ShowTaskOwnedVmObjects(task
, showonlytagged
=False):
4547 """ Routine to print out a summary listing of all the entries in a vm_map
4549 task - core.value : a object of type 'task *'
4553 taskobjq_total
= lambda:None
4554 taskobjq_total
.objects
= 0
4555 taskobjq_total
.vsize
= 0
4556 taskobjq_total
.rsize
= 0
4557 taskobjq_total
.wsize
= 0
4558 taskobjq_total
.csize
= 0
4559 vmo_list_head
= task
.task_objq
4560 vmo_ptr_type
= GetType('vm_object *')
4562 for vmo
in IterateQueue(vmo_list_head
, vmo_ptr_type
, "task_objq"):
4564 if not showonlytagged
or vmo
.vo_ledger_tag
!= 0:
4565 if taskobjq_total
.objects
== 0:
4567 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
4568 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
4569 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")
4570 ShowOwnedVmObject(vmo
, idx
, 0, taskobjq_total
)
4571 if taskobjq_total
.objects
!= 0:
4572 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
)
4575 def ShowOwnedVmObject(object, idx
, queue_len
, taskobjq_total
):
4576 """ Routine to print out a VM object owned by a task
4578 object - core.value : a object of type 'struct vm_object *'
4582 page_size
= kern
.globals.page_size
4583 if object.purgable
== 0:
4585 elif object.purgable
== 1:
4587 elif object.purgable
== 2:
4589 elif object.purgable
== 3:
4593 if object.pager
== 0:
4594 compressed_count
= 0
4596 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
4597 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
4599 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
))
4601 taskobjq_total
.objects
+= 1
4602 taskobjq_total
.vsize
+= object.vo_un1
.vou_size
/page_size
4603 taskobjq_total
.rsize
+= object.resident_page_count
4604 taskobjq_total
.wsize
+= object.wired_page_count
4605 taskobjq_total
.csize
+= compressed_count
4607 def GetProcPIDForObjectOwner(owner
):
4608 """ same as GetProcPIDForTask() but deals with -1 for a disowned object
4610 if unsigned(Cast(owner
, 'int')) == unsigned(int(0xffffffff)):
4612 return GetProcPIDForTask(owner
)
4614 def GetProcNameForObjectOwner(owner
):
4615 """ same as GetProcNameForTask() but deals with -1 for a disowned object
4617 if unsigned(Cast(owner
, 'int')) == unsigned(int(0xffffffff)):
4619 return GetProcNameForTask(owner
)
4621 def GetDescForNamedEntry(mem_entry
):
4623 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
.object)
4624 if mem_entry
.is_sub_map
:
4625 out_str
+= " is_sub_map"
4626 elif mem_entry
.is_copy
:
4627 out_str
+= " is_copy"
4629 out_str
+= " is_object"