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
= phys_footprint_entry
.le_credit
- phys_footprint_entry
.le_debit
63 if (now
- phys_footprint_entry
._le
.le_maxtracking
.le_peaks
[0].le_time
<= 1) and (phys_footprint_entry
._le
.le_maxtracking
.le_peaks
[0].le_max
> ledger_peak
):
64 ledger_peak
= phys_footprint_entry
._le
.le_maxtracking
.le_peaks
[0].le_max
67 @header("{: >8s} {: >12s} {: >12s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s} {: <20s}\n".format(
68 'pid', 'effective', 'requested', 'state', 'user_data', 'physical', 'iokit', 'footprint',
69 'spike', '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
= (task_physmem_footprint_ledger_entry
.le_credit
- task_physmem_footprint_ledger_entry
.le_debit
) / page_size
85 iokit_footprint
= (task_iokit_footprint_ledger_entry
.le_credit
- task_iokit_footprint_ledger_entry
.le_debit
) / page_size
86 phys_footprint
= (task_phys_footprint_ledger_entry
.le_credit
- task_phys_footprint_ledger_entry
.le_debit
) / page_size
87 phys_footprint_limit
= 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
= task_phys_footprint_ledger_entry
._le
.le_maxtracking
.le_lifetime_max
/ page_size
92 format_string
= '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: #011x} {5: >12d} {6: >10d} {7: >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_userdata
,
95 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} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^15s} {:<20s}".format(
239 'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ALL_FREE_PGS', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'WASTE', 'FLAGS', 'NAME'))
240 def GetZoneSummary(zone
):
241 """ Summarize a zone with important information. See help zprint for description of each field
243 zone: value - obj representing a zone in kernel
245 str - summary of the zone
248 format_string
= '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d} {markings} {name:s} '
249 pagesize
= kern
.globals.page_size
251 free_elements
= zone
.countfree
252 free_size
= free_elements
* zone
.elem_size
254 alloc_pages
= zone
.alloc_size
/ pagesize
255 alloc_count
= zone
.alloc_size
/ zone
.elem_size
256 alloc_waste
= zone
.alloc_size
% zone
.elem_size
259 ["collectable", "C"],
262 ["caller_acct", "@"],
263 ["exhaustible", "H"],
264 ["allows_foreign", "F"],
265 ["async_prio_refill", "R"],
268 ["doing_alloc_without_vm_priv", "A"],
269 ["doing_alloc_with_vm_priv", "S"],
272 if kern
.arch
== 'x86_64':
273 marks
.append(["gzalloc_exempt", "M"])
274 marks
.append(["alignment_required", "N"])
277 if not zone
.__getattr
__("zone_valid") :
280 if zone
.__getattr
__(mark
[0]) :
284 out_string
+= format_string
.format(zone
, zone
.cur_size
, zone
.page_count
,
285 zone
.count
, free_elements
, free_size
, zone
.count_all_free_pages
,
286 zone
.elem_size
, zone
.alloc_size
, alloc_count
,
287 alloc_pages
, alloc_waste
, name
= zone
.zone_name
, markings
=markings
)
289 if zone
.exhaustible
:
290 out_string
+= "(max: {:d})".format(zone
.max_size
)
294 @lldb_command('zprint')
295 def Zprint(cmd_args
=None):
296 """ Routine to print a summary listing of all the kernel zones
297 All columns are printed in decimal
301 $ - not encrypted during hibernation
302 @ - allocs and frees are accounted to caller process for KPRVT
304 F - allows foreign memory (memory not allocated from zone_map)
305 M - gzalloc will avoid monitoring this zone
306 R - will be refilled when below low water mark
307 O - does not allow refill callout to fill zone on noblock allocation
308 N - zone requires alignment (avoids padding this zone for debugging)
309 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
310 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
311 W - another thread is waiting for more memory
312 L - zone is being monitored by zleaks
313 G - currently running GC
314 I - zone was destroyed and is no longer valid
317 print GetZoneSummary
.header
318 for zval
in kern
.zones
:
319 print GetZoneSummary(zval
)
321 @xnudebug_test('test_zprint')
322 def TestZprint(kernel_target
, config
, lldb_obj
, isConnected
):
323 """ Test the functionality of zprint command
329 print "Target is not connected. Cannot test memstats"
331 res
= lldb
.SBCommandReturnObject()
332 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("zprint", res
)
333 result
= res
.GetOutput()
334 if len(result
.split("\n")) > 2:
342 # Macro: showzfreelist
344 def ShowZfreeListHeader(zone
):
345 """ Helper routine to print a header for zone freelist.
346 (Since the freelist does not have a custom type, this is not defined as a Type Summary).
348 zone:zone_t - Zone object to print header info
353 scaled_factor
= (unsigned(kern
.globals.zp_factor
) +
354 (unsigned(zone
.elem_size
) >> unsigned(kern
.globals.zp_scale
)))
357 out_str
+= "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
358 out_str
+= "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
359 zone
.elem_size
, zone
.count
, kern
.globals.zp_nopoison_cookie
, kern
.globals.zp_poisoned_cookie
, zone
.zp_count
, scaled_factor
)
360 out_str
+= "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
361 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
364 def ShowZfreeListChain(zone
, zfirst
, zlimit
):
365 """ Helper routine to print a zone free list chain
367 zone: zone_t - Zone object
368 zfirst: void * - A pointer to the first element of the free list chain
369 zlimit: int - Limit for the number of elements to be printed by showzfreelist
373 current
= Cast(zfirst
, 'void *')
374 while ShowZfreeList
.elts_found
< zlimit
:
375 ShowZfreeList
.elts_found
+= 1
376 znext
= dereference(Cast(current
, 'vm_offset_t *'))
377 znext
= (unsigned(znext
) ^
unsigned(kern
.globals.zp_nopoison_cookie
))
378 znext
= kern
.GetValueFromAddress(znext
, 'vm_offset_t *')
379 backup_ptr
= kern
.GetValueFromAddress((unsigned(Cast(current
, 'vm_offset_t')) + unsigned(zone
.elem_size
) - sizeof('vm_offset_t')), 'vm_offset_t *')
380 backup_val
= dereference(backup_ptr
)
381 n_unobfuscated
= (unsigned(backup_val
) ^
unsigned(kern
.globals.zp_nopoison_cookie
))
382 p_unobfuscated
= (unsigned(backup_val
) ^
unsigned(kern
.globals.zp_poisoned_cookie
))
384 if p_unobfuscated
== unsigned(znext
):
385 poison_str
= "P ({0: <d})".format(ShowZfreeList
.elts_found
- ShowZfreeList
.last_poisoned
)
386 ShowZfreeList
.last_poisoned
= ShowZfreeList
.elts_found
388 if n_unobfuscated
!= unsigned(znext
):
389 poison_str
= "INVALID"
390 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(
391 ShowZfreeList
.elts_found
, unsigned(current
), unsigned(znext
), unsigned(backup_val
), n_unobfuscated
, p_unobfuscated
, poison_str
)
392 if unsigned(znext
) == 0:
394 current
= Cast(znext
, 'void *')
396 @static_var('elts_found',0)
397 @static_var('last_poisoned',0)
398 @lldb_command('showzfreelist')
399 def ShowZfreeList(cmd_args
=None):
400 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
401 Usage: showzfreelist <zone> [iterations]
403 Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
406 print ShowZfreeList
.__doc
__
408 ShowZfreeList
.elts_found
= 0
409 ShowZfreeList
.last_poisoned
= 0
411 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
413 if len(cmd_args
) >= 2:
414 zlimit
= ArgumentStringToInt(cmd_args
[1])
415 ShowZfreeListHeader(zone
)
417 if unsigned(zone
.allows_foreign
) == 1:
418 for free_page_meta
in IterateQueue(zone
.pages
.any_free_foreign
, 'struct zone_page_metadata *', 'pages'):
419 if ShowZfreeList
.elts_found
== zlimit
:
421 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
422 if unsigned(zfirst
) != 0:
423 ShowZfreeListChain(zone
, zfirst
, zlimit
)
424 for free_page_meta
in IterateQueue(zone
.pages
.intermediate
, 'struct zone_page_metadata *', 'pages'):
425 if ShowZfreeList
.elts_found
== zlimit
:
427 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
428 if unsigned(zfirst
) != 0:
429 ShowZfreeListChain(zone
, zfirst
, zlimit
)
430 for free_page_meta
in IterateQueue(zone
.pages
.all_free
, 'struct zone_page_metadata *', 'pages'):
431 if ShowZfreeList
.elts_found
== zlimit
:
433 zfirst
= kern
.GetValueFromAddress(GetFreeList(free_page_meta
), 'void *')
434 if unsigned(zfirst
) != 0:
435 ShowZfreeListChain(zone
, zfirst
, zlimit
)
437 if ShowZfreeList
.elts_found
== zlimit
:
438 print "Stopped at {0: <d} elements!".format(zlimit
)
440 print "Found {0: <d} elements!".format(ShowZfreeList
.elts_found
)
442 # EndMacro: showzfreelist
444 # Macro: zstack_showzonesbeinglogged
446 @lldb_command('zstack_showzonesbeinglogged')
447 def ZstackShowZonesBeingLogged(cmd_args
=None):
448 """ Show all zones which have BTLog enabled.
451 for zval
in kern
.zones
:
453 print "Zone: %s with its BTLog at: 0x%lx" % (zval
.zone_name
, zval
.zlog_btlog
)
455 # EndMacro: zstack_showzonesbeinglogged
459 @lldb_command('zstack')
460 def Zstack(cmd_args
=None):
461 """ 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>.
462 Usage: zstack <btlog addr> <index> [<count>]
464 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
465 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
470 if int(kern
.globals.log_records
) == 0:
471 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
474 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
475 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
476 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
477 btrecords
= unsigned(btlog_ptr
.btrecords
)
478 btlog_size
= unsigned(sizeof('struct btlog'))
479 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
480 zstack_index
= ArgumentStringToInt(cmd_args
[1])
482 if len(cmd_args
) >= 3:
483 count
= ArgumentStringToInt(cmd_args
[2])
485 max_count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
487 if (zstack_index
+ count
) > max_count
:
488 count
= max_count
- zstack_index
490 while count
and (zstack_index
!= 0xffffff):
491 zstack_record_offset
= zstack_index
* btrecord_size
492 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
493 if int(zstack_record
.ref_count
)!=0:
494 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
500 # Macro: zstack_inorder
502 @lldb_command('zstack_inorder')
503 def ZstackInOrder(cmd_args
=None):
504 """ Zone leak debugging: Print the stack traces starting from head to the tail.
505 Usage: zstack_inorder <btlog addr>
508 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
510 if int(kern
.globals.log_records
) == 0:
511 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
514 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
515 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
516 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
517 btrecords
= unsigned(btlog_ptr
.btrecords
)
518 btlog_size
= unsigned(sizeof('struct btlog'))
519 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
520 zstack_head
= unsigned(btlog_ptr
.head
)
521 zstack_index
= zstack_head
522 zstack_tail
= unsigned(btlog_ptr
.tail
)
523 count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
525 while count
and (zstack_index
!= 0xffffff):
526 zstack_record_offset
= zstack_index
* btrecord_size
527 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
528 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
529 zstack_index
= zstack_record
.next
532 # EndMacro : zstack_inorder
536 @lldb_command('findoldest')
537 def FindOldest(cmd_args
=None):
540 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
542 # EndMacro : findoldest
544 # Macro : zstack_findleak
546 @lldb_command('zstack_findleak')
547 def zstack_findleak(cmd_args
=None):
548 """ Zone leak debugging: search the log and print the stack with the most active references
550 Usage: zstack_findleak <btlog address>
552 This is useful for verifying a suspected stack as being the source of
555 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
556 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
557 btrecords
= unsigned(btlog_ptr
.btrecords
)
559 cpcs_index
= unsigned(btlog_ptr
.head
)
560 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
565 while cpcs_index
!= 0xffffff:
566 cpcs_record_offset
= cpcs_index
* btrecord_size
567 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
568 if cpcs_record
.ref_count
> highref
:
569 highref_record
= cpcs_record
570 highref
= cpcs_record
.ref_count
571 highref_index
= cpcs_index
572 cpcs_index
= cpcs_record
.next
573 ShowZStackRecord(highref_record
, highref_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
575 # EndMacro: zstack_findleak
579 @lldb_command('findelem')
580 def FindElem(cmd_args
=None):
583 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
587 @lldb_command('zstack_findelem')
588 def ZStackFindElem(cmd_args
=None):
589 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
590 refer to the given zone element.
591 Usage: zstack_findelem <btlog addr> <elem addr>
593 When the kernel panics due to a corrupted zone element, get the
594 element address and use this command. This will show you the stack traces of all logged zalloc and
595 zfree operations which tells you who touched the element in the recent past. This also makes
596 double-frees readily apparent.
599 print ZStackFindElem
.__doc
__
601 if int(kern
.globals.log_records
) == 0 or unsigned(kern
.globals.corruption_debug_flag
) == 0:
602 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
605 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
606 target_element
= unsigned(kern
.GetValueFromAddress(cmd_args
[1], 'void *'))
608 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
609 btrecords
= unsigned(btlog_ptr
.btrecords
)
610 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
614 hashelem
= cast(btlog_ptr
.elem_linkage_un
.element_hash_queue
.tqh_first
, 'btlog_element_t *')
615 if (target_element
>> 32) != 0:
616 target_element
= target_element ^
0xFFFFFFFFFFFFFFFF
618 target_element
= target_element ^
0xFFFFFFFF
620 if unsigned(hashelem
.elem
) == target_element
:
621 recindex
= hashelem
.recindex
622 recoffset
= recindex
* btrecord_size
623 record
= kern
.GetValueFromAddress(btrecords
+ recoffset
, 'btlog_record_t *')
625 if record
.operation
== 1:
626 out_str
+= "OP: ALLOC. "
628 out_str
+= "OP: FREE. "
629 out_str
+= "Stack Index {0: <d} {1: <s}\n".format(recindex
, ('-' * 8))
631 print GetBtlogBacktrace(depth
, record
)
633 if int(record
.operation
) == prev_op
:
634 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
636 prev_op
= int(record
.operation
)
638 hashelem
= cast(hashelem
.element_hash_link
.tqe_next
, 'btlog_element_t *')
640 if scan_items
% 100 == 0:
641 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items
)
643 # EndMacro: zstack_findelem
645 @lldb_command('zstack_findtop', 'N:')
646 def ShowZstackTop(cmd_args
=None, cmd_options
={}):
647 """ Zone leak debugging: search the log and print the stacks with the most active references
650 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
654 raise ArgumentError('Missing required btlog address argument')
657 if '-N' in cmd_options
:
658 n
= int(cmd_options
['-N'])
660 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
661 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
662 btrecords
= unsigned(btlog_ptr
.btrecords
)
664 cpcs_index
= unsigned(btlog_ptr
.head
)
665 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
668 while cpcs_index
!= 0xffffff:
669 cpcs_record_offset
= cpcs_index
* btrecord_size
670 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
671 cpcs_record
.index
= cpcs_index
672 records
.append(cpcs_record
)
673 cpcs_index
= cpcs_record
.next
675 recs
= sorted(records
, key
=lambda x
: x
.ref_count
, reverse
=True)
678 ShowZStackRecord(rec
, rec
.index
, depth
, unsigned(btlog_ptr
.active_element_count
))
680 # EndMacro: zstack_findtop
684 @lldb_command('btlog_find', "AS")
685 def BtlogFind(cmd_args
=None, cmd_options
={}):
688 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
691 #EndMacro: btlog_find
695 @lldb_command('showzalloc')
696 def ShowZalloc(cmd_args
=None):
697 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
698 Usage: showzalloc <index>
701 print ShowZalloc
.__doc
__
703 if unsigned(kern
.globals.zallocations
) == 0:
704 print "zallocations array not initialized!"
706 zallocation
= kern
.globals.zallocations
[ArgumentStringToInt(cmd_args
[0])]
708 ShowZTrace([str(int(zallocation
.za_trace_index
))])
710 #EndMacro: showzalloc
714 @lldb_command('showztrace')
715 def ShowZTrace(cmd_args
=None):
716 """ Prints the backtrace from the ztraces array at index
717 Usage: showztrace <trace index>
720 print ShowZTrace
.__doc
__
722 if unsigned(kern
.globals.ztraces
) == 0:
723 print "ztraces array not initialized!"
725 ztrace_addr
= kern
.globals.ztraces
[ArgumentStringToInt(cmd_args
[0])]
727 ShowZstackTraceHelper(ztrace_addr
.zt_stack
, ztrace_addr
.zt_depth
)
729 #EndMacro: showztrace
731 #Macro: showztraceaddr
733 @lldb_command('showztraceaddr')
734 def ShowZTraceAddr(cmd_args
=None):
735 """ Prints the struct ztrace passed in.
736 Usage: showztraceaddr <trace address>
739 print ShowZTraceAddr
.__doc
__
741 ztrace_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'struct ztrace *')
742 print dereference(ztrace_ptr
)
743 ShowZstackTraceHelper(ztrace_ptr
.zt_stack
, ztrace_ptr
.zt_depth
)
745 #EndMacro: showztraceaddr
747 #Macro: showzstacktrace
749 @lldb_command('showzstacktrace')
750 def ShowZstackTrace(cmd_args
=None):
751 """ Routine to print a stacktrace stored by OSBacktrace.
752 Usage: showzstacktrace <saved stacktrace> [size]
754 size is optional, defaults to 15.
757 print ShowZstackTrace
.__doc
__
759 void_ptr_type
= gettype('void *')
760 void_double_ptr_type
= void_ptr_type
.GetPointerType()
761 trace
= kern
.GetValueFromAddress(cmd_args
[0], void_double_ptr_type
)
763 if len(cmd_args
) >= 2:
764 trace_size
= ArgumentStringToInt(cmd_args
[1])
765 ShowZstackTraceHelper(trace
, trace_size
)
767 #EndMacro: showzstacktrace
769 def ShowZstackTraceHelper(stack
, depth
):
770 """ Helper routine for printing a zstack.
772 stack: void *[] - An array of pointers representing the Zstack
773 depth: int - The depth of the ztrace stack
778 while trace_current
< depth
:
779 trace_addr
= stack
[trace_current
]
780 symbol_arr
= kern
.SymbolicateFromAddress(unsigned(trace_addr
))
782 symbol_str
= str(symbol_arr
[0].addr
)
785 print '{0: <#x} {1: <s}'.format(trace_addr
, symbol_str
)
788 #Macro: showtopztrace
790 @lldb_command('showtopztrace')
791 def ShowTopZtrace(cmd_args
=None):
792 """ Shows the ztrace with the biggest size.
793 (According to top_ztrace, not by iterating through the hash table)
795 top_trace
= kern
.globals.top_ztrace
796 print 'Index: {0: <d}'.format((unsigned(top_trace
) - unsigned(kern
.globals.ztraces
)) / sizeof('struct ztrace'))
797 print dereference(top_trace
)
798 ShowZstackTraceHelper(top_trace
.zt_stack
, top_trace
.zt_depth
)
800 #EndMacro: showtopztrace
804 @lldb_command('showzallocs')
805 def ShowZallocs(cmd_args
=None):
806 """ Prints all allocations in the zallocations table
808 if unsigned(kern
.globals.zallocations
) == 0:
809 print "zallocations array not initialized!"
811 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
813 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
815 while current_index
< max_zallocation
:
816 current_zalloc
= kern
.globals.zallocations
[current_index
]
817 if int(current_zalloc
.za_element
) != 0:
818 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
))
819 allocation_count
+= 1
821 print 'Total Allocations: {0: <d}'.format(allocation_count
)
823 #EndMacro: showzallocs
825 #Macro: showzallocsfortrace
827 @lldb_command('showzallocsfortrace')
828 def ShowZallocsForTrace(cmd_args
=None):
829 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
830 Usage: showzallocsfortrace <trace index>
833 print ShowZallocsForTrace
.__doc
__
835 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
836 target_index
= ArgumentStringToInt(cmd_args
[0])
838 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
840 while current_index
< max_zallocation
:
841 current_zalloc
= kern
.globals.zallocations
[current_index
]
842 if unsigned(current_zalloc
.za_element
) != 0 and (unsigned(current_zalloc
.za_trace_index
) == unsigned(target_index
)):
843 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index
, current_zalloc
.za_element
, current_zalloc
.za_size
)
844 allocation_count
+= 1
846 print 'Total Allocations: {0: <d}'.format(allocation_count
)
848 #EndMacro: showzallocsfortrace
852 @lldb_command('showztraces')
853 def ShowZTraces(cmd_args
=None):
854 """ Prints all traces with size > 0
856 ShowZTracesAbove([0])
858 #EndMacro: showztraces
860 #Macro: showztracesabove
862 @lldb_command('showztracesabove')
863 def ShowZTracesAbove(cmd_args
=None):
864 """ Prints all traces with size greater than X
865 Usage: showztracesabove <size>
868 print ShowZTracesAbove
.__doc
__
870 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
873 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
874 while current_index
< max_ztrace
:
875 ztrace_current
= kern
.globals.ztraces
[current_index
]
876 if ztrace_current
.zt_size
> unsigned(cmd_args
[0]):
877 print '{0: <5d} {1: <6d}'.format(current_index
, int(ztrace_current
.zt_size
))
880 print 'Total traces: {0: <d}'.format(ztrace_count
)
882 #EndMacro: showztracesabove
884 #Macro: showztracehistogram
886 @lldb_command('showztracehistogram')
887 def ShowZtraceHistogram(cmd_args
=None):
888 """ Prints the histogram of the ztrace table
890 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
893 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
894 while current_index
< max_ztrace
:
895 ztrace_current
= kern
.globals.ztraces
[current_index
]
896 if ztrace_current
.zt_hit_count
!= 0:
897 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index
, ztrace_current
.zt_hit_count
, ztrace_current
.zt_collisions
)
900 print 'Total traces: {0: <d}'.format(ztrace_count
)
902 #EndMacro: showztracehistogram
904 #Macro: showzallochistogram
906 @lldb_command('showzallochistogram')
907 def ShowZallocHistogram(cmd_args
=None):
908 """ Prints the histogram for the zalloc table
910 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
912 zallocation_count
= 0
913 max_ztrace
= unsigned(kern
.globals.zleak_alloc_buckets
)
914 while current_index
< max_ztrace
:
915 zallocation_current
= kern
.globals.zallocations
[current_index
]
916 if zallocation_current
.za_hit_count
!= 0:
917 print '{0: <5d} {1: <9d}'.format(current_index
, zallocation_current
.za_hit_count
)
918 zallocation_count
+= 1
920 print 'Total Allocations: {0: <d}'.format(zallocation_count
)
922 #EndMacro: showzallochistogram
926 @lldb_command('showzstats')
927 def ShowZstats(cmd_args
=None):
928 """ Prints the zone leak detection stats
930 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern
.globals.z_alloc_collisions
), unsigned(kern
.globals.z_trace_collisions
))
931 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern
.globals.z_alloc_overwrites
), unsigned(kern
.globals.z_trace_overwrites
))
932 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern
.globals.z_alloc_recorded
), unsigned(kern
.globals.z_trace_recorded
))
934 #EndMacro: showzstats
936 def GetBtlogBacktrace(depth
, zstack_record
):
937 """ Helper routine for getting a BT Log record backtrace stack.
939 depth:int - The depth of the zstack record
940 zstack_record:btlog_record_t * - A BTLog record
942 str - string with backtrace in it.
946 if not zstack_record
:
947 return "Zstack record none!"
949 depth_val
= unsigned(depth
)
950 while frame
< depth_val
:
951 frame_pc
= zstack_record
.bt
[frame
]
952 if not frame_pc
or int(frame_pc
) == 0:
954 symbol_arr
= kern
.SymbolicateFromAddress(frame_pc
)
956 symbol_str
= str(symbol_arr
[0].addr
)
959 out_str
+= "{0: <#0x} <{1: <s}>\n".format(frame_pc
, symbol_str
)
963 def ShowZStackRecord(zstack_record
, zstack_index
, btrecord_btdepth
, elements_count
):
964 """ Helper routine for printing a single zstack record
966 zstack_record:btlog_record_t * - A BTLog record
967 zstack_index:int - Index for the record in the BTLog table
972 if zstack_record
.operation
== 1:
976 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))
978 print GetBtlogBacktrace(btrecord_btdepth
, zstack_record
)
983 @lldb_command('showioalloc')
984 def ShowIOAllocations(cmd_args
=None):
985 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
986 Routine to display a summary of memory accounting allocated by IOKit allocators.
988 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_ivars_size
, (kern
.globals.debug_ivars_size
/ 1024))
989 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_container_malloc_size
, (kern
.globals.debug_container_malloc_size
/ 1024))
990 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomalloc_size
, (kern
.globals.debug_iomalloc_size
/ 1024))
991 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomallocpageable_size
, (kern
.globals.debug_iomallocpageable_size
/ 1024))
994 # EndMacro: showioalloc
997 # Macro: showselectmem
998 @lldb_command('showselectmem', "S:")
999 def ShowSelectMem(cmd_args
=None, cmd_options
={}):
1000 """ Show memory cached by threads on calls to select.
1002 usage: showselectmem [-v]
1003 -v : print each thread's memory
1004 (one line per thread with non-zero select memory)
1005 -S {addr} : Find the thread whose thread-local select set
1006 matches the given address
1010 if config
['verbosity'] > vHUMAN
:
1012 if "-S" in cmd_options
:
1013 opt_wqs
= unsigned(kern
.GetValueFromAddress(cmd_options
["-S"], 'uint64_t *'))
1015 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options
["-S"]))
1018 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1019 for t
in kern
.tasks
:
1020 for th
in IterateQueue(t
.threads
, 'thread *', 'task_threads'):
1021 uth
= Cast(th
.uthread
, 'uthread *');
1023 if hasattr(uth
, 'uu_allocsize'): # old style
1024 thmem
= uth
.uu_allocsize
1026 elif hasattr(uth
, 'uu_wqstate_sz'): # new style
1027 thmem
= uth
.uu_wqstate_sz
1030 print "What kind of uthread is this?!"
1032 if opt_wqs
and opt_wqs
== unsigned(wqs
):
1033 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs
, unsigned(th
), unsigned(th
.thread_id
))
1034 if verbose
and thmem
> 0:
1035 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t
), unsigned(th
.thread_id
), thmem
)
1038 print "Total: {:d} bytes ({:d} kbytes)".format(selmem
, selmem
/1024)
1039 # Endmacro: showselectmem
1042 # Macro: showtaskvme
1043 @lldb_command('showtaskvme', "PS")
1044 def ShowTaskVmeHelper(cmd_args
=None, cmd_options
={}):
1045 """ Display a summary list of the specified vm_map's entries
1046 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
1047 Use -S flag to show VM object shadow chains
1048 Use -P flag to show pager info (mapped file, compressed pages, ...)
1050 show_pager_info
= False
1051 show_all_shadows
= False
1052 if "-P" in cmd_options
:
1053 show_pager_info
= True
1054 if "-S" in cmd_options
:
1055 show_all_shadows
= True
1056 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1057 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1059 @lldb_command('showallvme', "PS")
1060 def ShowAllVME(cmd_args
=None, cmd_options
={}):
1061 """ Routine to print a summary listing of all the vm map entries
1062 Go Through each task in system and show the vm memory regions
1063 Use -S flag to show VM object shadow chains
1064 Use -P flag to show pager info (mapped file, compressed pages, ...)
1066 show_pager_info
= False
1067 show_all_shadows
= False
1068 if "-P" in cmd_options
:
1069 show_pager_info
= True
1070 if "-S" in cmd_options
:
1071 show_all_shadows
= True
1072 for task
in kern
.tasks
:
1073 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1075 @lldb_command('showallvm')
1076 def ShowAllVM(cmd_args
=None):
1077 """ Routine to print a summary listing of all the vm maps
1079 for task
in kern
.tasks
:
1080 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1081 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1082 print GetVMMapSummary
.header
1083 print GetVMMapSummary(task
.map)
1085 @lldb_command("showtaskvm")
1086 def ShowTaskVM(cmd_args
=None):
1087 """ Display info about the specified task's vm_map
1088 syntax: (lldb) showtaskvm <task_ptr>
1091 print ShowTaskVM
.__doc
__
1093 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1095 print "Unknown arguments."
1097 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1098 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1099 print GetVMMapSummary
.header
1100 print GetVMMapSummary(task
.map)
1103 @lldb_command('showallvmstats')
1104 def ShowAllVMStats(cmd_args
=None):
1105 """ Print a summary of vm statistics in a table format
1107 page_size
= kern
.globals.page_size
1108 vmstats
= lambda:None
1109 vmstats
.wired_count
= 0
1110 vmstats
.resident_count
= 0
1111 vmstats
.resident_max
= 0
1112 vmstats
.internal
= 0
1113 vmstats
.external
= 0
1114 vmstats
.reusable
= 0
1115 vmstats
.compressed
= 0
1116 vmstats
.compressed_peak
= 0
1117 vmstats
.compressed_lifetime
= 0
1120 hdr_format
= "{0: >10s} {1: <20s} {2: >6s} {3: >10s} {4: >10s} {5: >10s} {6: >10s} {7: >10s} {8: >10s} {9: >10s} {10: >10s} {11: >10s} {12: >10s} {13: >10s} {14:}"
1121 print hdr_format
.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '')
1122 print hdr_format
.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '')
1123 entry_format
= "{p.p_pid: >10d} {p.p_comm: <20s} {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} {s.error}"
1125 for task
in kern
.tasks
:
1126 proc
= Cast(task
.bsd_info
, 'proc *')
1127 vmmap
= Cast(task
.map, '_vm_map *')
1129 vmstats
.wired_count
= vmmap
.pmap
.stats
.wired_count
;
1130 vmstats
.resident_count
= unsigned(vmmap
.pmap
.stats
.resident_count
);
1131 vmstats
.resident_max
= vmmap
.pmap
.stats
.resident_max
;
1132 vmstats
.internal
= unsigned(vmmap
.pmap
.stats
.internal
);
1133 vmstats
.external
= unsigned(vmmap
.pmap
.stats
.external
);
1134 vmstats
.reusable
= unsigned(vmmap
.pmap
.stats
.reusable
);
1135 vmstats
.compressed
= unsigned(vmmap
.pmap
.stats
.compressed
);
1136 vmstats
.compressed_peak
= unsigned(vmmap
.pmap
.stats
.compressed_peak
);
1137 vmstats
.compressed_lifetime
= unsigned(vmmap
.pmap
.stats
.compressed_lifetime
);
1138 vmstats
.new_resident_count
= vmstats
.internal
+ vmstats
.external
1140 if vmstats
.internal
< 0:
1141 vmstats
.error
+= '*'
1142 if vmstats
.external
< 0:
1143 vmstats
.error
+= '*'
1144 if vmstats
.reusable
< 0:
1145 vmstats
.error
+= '*'
1146 if vmstats
.compressed
< 0:
1147 vmstats
.error
+= '*'
1148 if vmstats
.compressed_peak
< 0:
1149 vmstats
.error
+= '*'
1150 if vmstats
.compressed_lifetime
< 0:
1151 vmstats
.error
+= '*'
1152 if vmstats
.new_resident_count
+vmstats
.reusable
!= vmstats
.resident_count
:
1153 vmstats
.error
+= '*'
1155 print entry_format
.format(p
=proc
, m
=vmmap
, vsize
=(unsigned(vmmap
.size
) / page_size
), t
=task
, s
=vmstats
)
1158 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
1159 """ Routine to print out a summary listing of all the entries in a vm_map
1161 task - core.value : a object of type 'task *'
1165 print "vm_map entries for task " + hex(task
)
1166 print GetTaskSummary
.header
1167 print GetTaskSummary(task
)
1169 print "Task {0: <#020x} has map = 0x0"
1171 print GetVMMapSummary
.header
1172 print GetVMMapSummary(task
.map)
1173 vme_list_head
= task
.map.hdr
.links
1174 vme_ptr_type
= GetType('vm_map_entry *')
1175 print GetVMEntrySummary
.header
1176 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1177 print GetVMEntrySummary(vme
, show_pager_info
, show_all_shadows
)
1180 @lldb_command("showmap")
1181 def ShowMap(cmd_args
=None):
1182 """ Routine to print out info about the specified vm_map
1183 usage: showmap <vm_map>
1185 if cmd_args
== None or len(cmd_args
) < 1:
1186 print "Invalid argument.", ShowMap
.__doc
__
1188 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1189 print GetVMMapSummary
.header
1190 print GetVMMapSummary(map_val
)
1192 @lldb_command("showmapvme")
1193 def ShowMapVME(cmd_args
=None):
1194 """Routine to print out info about the specified vm_map and its vm entries
1195 usage: showmapvme <vm_map>
1197 if cmd_args
== None or len(cmd_args
) < 1:
1198 print "Invalid argument.", ShowMap
.__doc
__
1200 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1201 print GetVMMapSummary
.header
1202 print GetVMMapSummary(map_val
)
1203 vme_list_head
= map_val
.hdr
.links
1204 vme_ptr_type
= GetType('vm_map_entry *')
1205 print GetVMEntrySummary
.header
1206 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1207 print GetVMEntrySummary(vme
)
1210 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
1211 @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"))
1212 def GetVMMapSummary(vmmap
):
1213 """ Display interesting bits from vm_map struct """
1215 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
1216 vm_size
= uint64_t(vmmap
.size
).value
1218 if vmmap
.pmap
!= 0: resident_pages
= int(vmmap
.pmap
.stats
.resident_count
)
1220 if int(vmmap
.holelistenabled
) == 0: first_free
= vmmap
.f_s
.first_free
1221 out_string
+= format_string
.format(vmmap
, vmmap
.pmap
, vm_size
, vmmap
.hdr
.nentries
, resident_pages
, vmmap
.hint
, first_free
)
1224 @lldb_type_summary(['vm_map_entry'])
1225 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1226 def GetVMEntrySummary(vme
):
1227 """ Display vm entry specific information. """
1228 page_size
= kern
.globals.page_size
1230 format_string
= "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1231 vme_protection
= int(vme
.protection
)
1232 vme_max_protection
= int(vme
.max_protection
)
1233 vme_extra_info_str
="SC-Ds"[int(vme
.inheritance
)]
1234 if int(vme
.is_sub_map
) != 0 :
1235 vme_extra_info_str
+="s"
1236 elif int(vme
.needs_copy
) != 0 :
1237 vme_extra_info_str
+="n"
1238 num_pages
= (unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)) / page_size
1239 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
)
1242 # EndMacro: showtaskvme
1243 @lldb_command('showmapwired')
1244 def ShowMapWired(cmd_args
=None):
1245 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1247 if cmd_args
== None or len(cmd_args
) < 1:
1248 print "Invalid argument", ShowMapWired
.__doc
__
1250 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1253 @lldb_type_summary(['kmod_info_t *'])
1254 @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'))
1255 def GetKextSummary(kmod
):
1256 """ returns a string representation of kext information
1259 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1260 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod
.address
), unsigned(kmod
.size
))
1261 text_segment
= macho
.get_text_segment(segments
)
1262 if not text_segment
:
1263 text_segment
= segments
[0]
1264 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
)
1267 @lldb_type_summary(['uuid_t'])
1269 def GetUUIDSummary(uuid
):
1270 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1272 arr
= Cast(addressof(uuid
), 'uint8_t *')
1275 data
.append(int(arr
[i
]))
1276 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
)
1278 @lldb_command('showallkmods')
1279 def ShowAllKexts(cmd_args
=None):
1280 """Display a summary listing of all loaded kexts (alias: showallkmods)
1282 kmod_val
= kern
.globals.kmod
1283 kextuuidinfo
= GetKextLoadInformation(show_progress
=(config
['verbosity'] > vHUMAN
))
1284 print "{: <36s} ".format("UUID") + GetKextSummary
.header
1285 for kval
in IterateLinkedList(kmod_val
, 'next'):
1286 uuid
= "........-....-....-....-............"
1287 kaddr
= unsigned(kval
.address
)
1288 found_kext_summary
= None
1289 for l
in kextuuidinfo
:
1290 if kaddr
== int(l
[3],16):
1292 found_kext_summary
= l
1294 if found_kext_summary
:
1295 _ksummary
= GetKextSummary(found_kext_summary
[7])
1297 _ksummary
= GetKextSummary(kval
)
1298 print uuid
+ " " + _ksummary
1300 def GetKmodWithAddr(addr
):
1301 """ Go through kmod list and find one with begin_addr as addr
1302 returns: None if not found. else a cvalue of type kmod
1304 kmod_val
= kern
.globals.kmod
1305 for kval
in IterateLinkedList(kmod_val
, 'next'):
1306 if addr
== unsigned(kval
.address
):
1310 def GetAllSegmentsAndSectionsFromDataInMemory(address
, size
):
1311 """ reads memory at address and parses mach_header to get segment and section information
1312 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
1313 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
1314 if TEXT segment is not found a dummy segment & section with address, size is returned.
1316 cache_hash
= "kern.kexts.segments.{}.{}".format(address
, size
)
1317 cached_result
= caching
.GetDynamicCacheData(cache_hash
,())
1319 return cached_result
1321 defval
= macho
.MachOSegment('__TEXT', address
, size
, 0, size
)
1322 if address
== 0 or size
== 0:
1323 return ([defval
], [defval
])
1325 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
1326 # until we have separate version. we will pay penalty only on arm64 devices
1327 if not kern
.arch
.startswith('arm64'):
1328 return ([defval
], [defval
])
1330 restrict_size_to_read
= 1536
1332 while machoObject
is None:
1333 err
= lldb
.SBError()
1334 size_to_read
= min(size
, restrict_size_to_read
)
1335 data
= LazyTarget
.GetProcess().ReadMemory(address
, size_to_read
, err
)
1336 if not err
.Success():
1337 print "Failed to read memory at {} and size {}".format(address
, size_to_read
)
1338 return ([defval
], [defval
])
1340 m
= macho
.MemMacho(data
, len(data
))
1342 except Exception as e
:
1343 if str(e
.message
).find('unpack requires a string argument') >= 0:
1344 # this may be due to short read of memory. Lets do double read size.
1345 restrict_size_to_read
*= 2
1346 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read
))
1349 print "Failed to read MachO for address {} errormessage: {}".format(address
, e
.message
)
1350 return ([defval
], [defval
])
1351 # end of while loop. We have machoObject defined
1352 segments
= machoObject
.get_segments_with_name('')
1353 sections
= machoObject
.get_sections_with_name('')
1354 rval
= (segments
, sections
)
1355 caching
.SaveDynamicCacheData(cache_hash
, rval
)
1358 def GetKextLoadInformation(addr
=0, show_progress
=False):
1359 """ Extract the kext uuid and load address information from the kernel data structure.
1361 addr - int - optional integer that is the address to search for.
1363 [] - array with each entry of format
1364 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
1365 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
1367 cached_result
= caching
.GetDynamicCacheData("kern.kexts.loadinformation", [])
1368 ## if specific addr is provided then ignore caching
1369 if cached_result
and not addr
:
1370 return cached_result
1372 # because of <rdar://problem/12683084>, we can't find summaries directly
1373 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1374 baseaddr
= unsigned(kern
.globals.gLoadedKextSummaries
) + 0x10
1375 summaries_begin
= kern
.GetValueFromAddress(baseaddr
, 'OSKextLoadedKextSummary *')
1376 total_summaries
= int(kern
.globals.gLoadedKextSummaries
.numSummaries
)
1377 kext_version
= int(kern
.globals.gLoadedKextSummaries
.version
)
1378 entry_size
= 64 + 16 + 8 + 8 + 8 + 4 + 4
1379 if kext_version
>= 2 :
1380 entry_size
= int(kern
.globals.gLoadedKextSummaries
.entry_size
)
1382 for i
in range(total_summaries
):
1384 print "progress: {}/{}".format(i
, total_summaries
)
1385 tmpaddress
= unsigned(summaries_begin
) + (i
* entry_size
)
1386 current_kext
= kern
.GetValueFromAddress(tmpaddress
, 'OSKextLoadedKextSummary *')
1387 # code to extract macho information
1388 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext
.address
), unsigned(current_kext
.size
))
1389 seginfo
= macho
.get_text_segment(segments
)
1391 seginfo
= segments
[0]
1392 kmod_obj
= GetKmodWithAddr(unsigned(current_kext
.address
))
1394 if addr
== unsigned(current_kext
.address
) or addr
== seginfo
.vmaddr
:
1395 return [(GetUUIDSummary(current_kext
.uuid
) , hex(seginfo
.vmaddr
).rstrip('L'), str(current_kext
.name
), hex(current_kext
.address
), segments
, seginfo
, current_kext
, kmod_obj
)]
1396 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
))
1399 caching
.SaveDynamicCacheData("kern.kexts.loadinformation", retval
)
1402 lldb_alias('showallkexts', 'showallkmods')
1404 def GetOSKextVersion(version_num
):
1405 """ returns a string of format 1.2.3x from the version_num
1406 params: version_num - int
1409 if version_num
== -1 :
1411 (MAJ_MULT
, MIN_MULT
, REV_MULT
,STAGE_MULT
) = (100000000, 1000000, 10000, 1000)
1412 version
= version_num
1414 vers_major
= version
/ MAJ_MULT
1415 version
= version
- (vers_major
* MAJ_MULT
)
1417 vers_minor
= version
/ MIN_MULT
1418 version
= version
- (vers_minor
* MIN_MULT
)
1420 vers_revision
= version
/ REV_MULT
1421 version
= version
- (vers_revision
* REV_MULT
)
1423 vers_stage
= version
/ STAGE_MULT
1424 version
= version
- (vers_stage
* STAGE_MULT
)
1426 vers_stage_level
= version
1428 out_str
= "%d.%d" % (vers_major
, vers_minor
)
1429 if vers_revision
> 0: out_str
+= ".%d" % vers_revision
1430 if vers_stage
== 1 : out_str
+= "d%d" % vers_stage_level
1431 if vers_stage
== 3 : out_str
+= "a%d" % vers_stage_level
1432 if vers_stage
== 5 : out_str
+= "b%d" % vers_stage_level
1433 if vers_stage
== 6 : out_str
+= "fc%d" % vers_stage_level
1437 @lldb_command('showallknownkmods')
1438 def ShowAllKnownKexts(cmd_args
=None):
1439 """ Display a summary listing of all kexts known in the system.
1440 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1442 kext_count
= int(kern
.globals.sKextsByID
.count
)
1444 kext_dictionary
= kern
.globals.sKextsByID
.dictionary
1445 print "%d kexts in sKextsByID:" % kext_count
1446 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1447 format_string
= "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1449 while index
< kext_count
:
1450 kext_dict
= GetObjectAtIndexFromArray(kext_dictionary
, index
)
1451 kext_name
= str(kext_dict
.key
.string
)
1452 osk
= Cast(kext_dict
.value
, 'OSKext *')
1453 if int(osk
.flags
.loaded
) :
1454 load_addr
= "{0: <#020x}".format(osk
.kmod_info
)
1455 id = "{0: >5d}".format(osk
.loadTag
)
1457 load_addr
= "------"
1459 version_num
= unsigned(osk
.version
)
1460 version
= GetOSKextVersion(version_num
)
1461 print format_string
.format(osk
, load_addr
, id, version
, kext_name
)
1466 def FindKmodNameForAddr(addr
):
1467 """ Given an address, return the name of the kext containing that address
1469 addr
= unsigned(addr
)
1470 all_kexts_info
= GetKextLoadInformation()
1471 for kinfo
in all_kexts_info
:
1472 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1474 return kinfo
[7].name
1478 @lldb_command('addkextaddr')
1479 def AddKextAddr(cmd_args
=[]):
1480 """ Given an address, load the kext which contains that address
1481 Syntax: (lldb) addkextaddr <addr>
1483 if len(cmd_args
) < 1:
1484 raise ArgumentError("Insufficient arguments")
1486 addr
= ArgumentStringToInt(cmd_args
[0])
1487 all_kexts_info
= GetKextLoadInformation()
1489 found_segment
= None
1490 for kinfo
in all_kexts_info
:
1491 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1493 print GetKextSummary
.header
1494 print GetKextSummary(kinfo
[7]) + " segment: {} offset = {:#0x}".format(segment
.name
, (addr
- segment
.vmaddr
))
1495 cur_uuid
= kinfo
[0].lower()
1496 print "Fetching dSYM for %s" % cur_uuid
1497 info
= dsymForUUID(cur_uuid
)
1498 if info
and 'DBGSymbolRichExecutable' in info
:
1499 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
1500 addDSYM(cur_uuid
, info
)
1501 loadDSYM(cur_uuid
, int(kinfo
[1],16), kinfo
[4])
1503 print "Failed to get symbol info for %s" % cur_uuid
1507 @lldb_command('showkmodaddr')
1508 def ShowKmodAddr(cmd_args
=[]):
1509 """ Given an address, print the offset and name for the kmod containing it
1510 Syntax: (lldb) showkmodaddr <addr>
1512 if len(cmd_args
) < 1:
1513 raise ArgumentError("Insufficient arguments")
1515 addr
= ArgumentStringToInt(cmd_args
[0])
1516 all_kexts_info
= GetKextLoadInformation()
1518 found_segment
= None
1519 for kinfo
in all_kexts_info
:
1520 s
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1526 print GetKextSummary
.header
1527 print GetKextSummary(found_kinfo
[7]) + " segment: {} offset = {:#0x}".format(found_segment
.name
, (addr
- found_segment
.vmaddr
))
1532 @lldb_command('addkext','AF:N:')
1533 def AddKextSyms(cmd_args
=[], cmd_options
={}):
1534 """ Add kext symbols into lldb.
1535 This command finds symbols for a uuid and load the required executable
1537 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1538 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1539 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
1540 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
1541 addkext all : Will load all the kext symbols - SLOW
1545 if "-F" in cmd_options
:
1546 exec_path
= cmd_options
["-F"]
1547 exec_full_path
= ResolveFSPath(exec_path
)
1548 if not os
.path
.exists(exec_full_path
):
1549 raise ArgumentError("Unable to resolve {:s}".format(exec_path
))
1551 if not os
.path
.isfile(exec_full_path
):
1552 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1553 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1554 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path
))
1559 slide_value
= cmd_args
[0]
1560 debuglog("loading slide value from user input %s" % cmd_args
[0])
1562 filespec
= lldb
.SBFileSpec(exec_full_path
, False)
1563 print "target modules add %s" % exec_full_path
1564 print lldb_run_command("target modules add %s" % exec_full_path
)
1565 loaded_module
= LazyTarget
.GetTarget().FindModule(filespec
)
1566 if loaded_module
.IsValid():
1567 uuid_str
= loaded_module
.GetUUIDString()
1568 debuglog("added module %s with uuid %s" % (exec_full_path
, uuid_str
))
1569 if slide_value
is None:
1570 all_kexts_info
= GetKextLoadInformation()
1571 for k
in all_kexts_info
:
1573 if k
[0].lower() == uuid_str
.lower():
1576 debuglog("found the slide %s for uuid %s" % (k
[1], k
[0]))
1577 if slide_value
is None:
1578 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path
)
1581 cmd_str
= "target modules load --file %s --slide %s" % ( exec_full_path
, str(slide_value
))
1584 cmd_str
= "target modules load --file {} ".format(exec_full_path
)
1587 sections_str
+= " {} {:#0x} ".format(s
.name
, s
.vmaddr
)
1588 cmd_str
+= sections_str
1591 lldb
.debugger
.HandleCommand(cmd_str
)
1593 kern
.symbolicator
= None
1596 all_kexts_info
= GetKextLoadInformation()
1598 if "-N" in cmd_options
:
1599 kext_name
= cmd_options
["-N"]
1600 kext_name_matches
= GetLongestMatchOption(kext_name
, [str(x
[2]) for x
in all_kexts_info
], True)
1601 if len(kext_name_matches
) != 1 and "-A" not in cmd_options
:
1602 print "Ambiguous match for name: {:s}".format(kext_name
)
1603 if len(kext_name_matches
) > 0:
1604 print "Options are:\n\t" + "\n\t".join(kext_name_matches
)
1606 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches
[0], kext_name
))
1607 for cur_knm
in kext_name_matches
:
1608 for x
in all_kexts_info
:
1610 cur_uuid
= x
[0].lower()
1611 print "Fetching dSYM for {:s}".format(cur_uuid
)
1612 info
= dsymForUUID(cur_uuid
)
1613 if info
and 'DBGSymbolRichExecutable' in info
:
1614 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid
, info
['DBGSymbolRichExecutable'])
1615 addDSYM(cur_uuid
, info
)
1616 loadDSYM(cur_uuid
, int(x
[1],16), x
[4])
1618 print "Failed to get symbol info for {:s}".format(cur_uuid
)
1620 kern
.symbolicator
= None
1623 if len(cmd_args
) < 1:
1624 raise ArgumentError("No arguments specified.")
1626 uuid
= cmd_args
[0].lower()
1628 load_all_kexts
= False
1630 load_all_kexts
= True
1632 if not load_all_kexts
and len(uuid_regex
.findall(uuid
)) == 0:
1633 raise ArgumentError("Unknown argument {:s}".format(uuid
))
1635 for k_info
in all_kexts_info
:
1636 cur_uuid
= k_info
[0].lower()
1637 if load_all_kexts
or (uuid
== cur_uuid
):
1638 print "Fetching dSYM for %s" % cur_uuid
1639 info
= dsymForUUID(cur_uuid
)
1640 if info
and 'DBGSymbolRichExecutable' in info
:
1641 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
1642 addDSYM(cur_uuid
, info
)
1643 loadDSYM(cur_uuid
, int(k_info
[1],16), k_info
[4])
1645 print "Failed to get symbol info for %s" % cur_uuid
1647 kern
.symbolicator
= None
1652 lldb_alias('showkmod', 'showkmodaddr')
1653 lldb_alias('showkext', 'showkmodaddr')
1654 lldb_alias('showkextaddr', 'showkmodaddr')
1656 @lldb_type_summary(['mount *'])
1657 @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'))
1658 def GetMountSummary(mount
):
1659 """ Display a summary of mount on the system
1661 out_string
= ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1662 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
1663 "{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'))
1666 @lldb_command('showallmounts')
1667 def ShowAllMounts(cmd_args
=None):
1668 """ Print all mount points
1670 mntlist
= kern
.globals.mountlist
1671 print GetMountSummary
.header
1672 for mnt
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
1673 print GetMountSummary(mnt
)
1676 lldb_alias('ShowAllVols', 'showallmounts')
1678 @lldb_command('systemlog')
1679 def ShowSystemLog(cmd_args
=None):
1680 """ Display the kernel's printf ring buffer """
1681 msgbufp
= kern
.globals.msgbufp
1682 msg_size
= int(msgbufp
.msg_size
)
1683 msg_bufx
= int(msgbufp
.msg_bufx
)
1684 msg_bufr
= int(msgbufp
.msg_bufr
)
1685 msg_bufc
= msgbufp
.msg_bufc
1686 msg_bufc_data
= msg_bufc
.GetSBValue().GetPointeeData(0, msg_size
)
1688 # the buffer is circular; start at the write pointer to end,
1689 # then from beginning to write pointer
1691 err
= lldb
.SBError()
1692 for i
in range(msg_bufx
, msg_size
) + range(0, msg_bufx
) :
1694 cbyte
= msg_bufc_data
.GetUnsignedInt8(err
, i
)
1695 if not err
.Success() :
1696 raise ValueError("Failed to read character at offset " + str(i
) + ": " + err
.GetCString())
1711 @static_var('output','')
1712 def _GetVnodePathName(vnode
, vnodename
):
1713 """ Internal function to get vnode path string from vnode structure.
1717 returns Nothing. The output will be stored in the static variable.
1721 if int(vnode
.v_flag
) & 0x1 and int(hex(vnode
.v_mount
), 16) !=0:
1722 if int(vnode
.v_mount
.mnt_vnodecovered
):
1723 _GetVnodePathName(vnode
.v_mount
.mnt_vnodecovered
, str(vnode
.v_mount
.mnt_vnodecovered
.v_name
) )
1725 _GetVnodePathName(vnode
.v_parent
, str(vnode
.v_parent
.v_name
))
1726 _GetVnodePathName
.output
+= "/%s" % vnodename
1728 def GetVnodePath(vnode
):
1729 """ Get string representation of the vnode
1730 params: vnodeval - value representing vnode * in the kernel
1731 return: str - of format /path/to/something
1735 if (int(vnode
.v_flag
) & 0x000001) and int(hex(vnode
.v_mount
), 16) != 0 and (int(vnode
.v_mount
.mnt_flag
) & 0x00004000) :
1738 _GetVnodePathName
.output
= ''
1739 if abs(vnode
.v_name
) != 0:
1740 _GetVnodePathName(vnode
, str(vnode
.v_name
))
1741 out_str
+= _GetVnodePathName
.output
1743 out_str
+= 'v_name = NULL'
1744 _GetVnodePathName
.output
= ''
1748 @lldb_command('showvnodepath')
1749 def ShowVnodePath(cmd_args
=None):
1750 """ Prints the path for a vnode
1751 usage: showvnodepath <vnode>
1753 if cmd_args
!= None and len(cmd_args
) > 0 :
1754 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
1756 print GetVnodePath(vnode_val
)
1759 # Macro: showvnodedev
1760 def GetVnodeDevInfo(vnode
):
1761 """ Internal function to get information from the device type vnodes
1762 params: vnode - value representing struct vnode *
1763 return: str - formatted output information for block and char vnode types passed as param
1765 vnodedev_output
= ""
1766 vblk_type
= GetEnumValue('vtype::VBLK')
1767 vchr_type
= GetEnumValue('vtype::VCHR')
1768 if (vnode
.v_type
== vblk_type
) or (vnode
.v_type
== vchr_type
):
1769 devnode
= Cast(vnode
.v_data
, 'devnode_t *')
1770 devnode_dev
= devnode
.dn_typeinfo
.dev
1771 devnode_major
= (devnode_dev
>> 24) & 0xff
1772 devnode_minor
= devnode_dev
& 0x00ffffff
1774 # boilerplate device information for a vnode
1775 vnodedev_output
+= "Device Info:\n\t vnode:\t\t{:#x}".format(vnode
)
1776 vnodedev_output
+= "\n\t type:\t\t"
1777 if (vnode
.v_type
== vblk_type
):
1778 vnodedev_output
+= "VBLK"
1779 if (vnode
.v_type
== vchr_type
):
1780 vnodedev_output
+= "VCHR"
1781 vnodedev_output
+= "\n\t name:\t\t{:<s}".format(vnode
.v_name
)
1782 vnodedev_output
+= "\n\t major, minor:\t{:d},{:d}".format(devnode_major
, devnode_minor
)
1783 vnodedev_output
+= "\n\t mode\t\t0{:o}".format(unsigned(devnode
.dn_mode
))
1784 vnodedev_output
+= "\n\t owner (u,g):\t{:d} {:d}".format(devnode
.dn_uid
, devnode
.dn_gid
)
1786 # decode device specific data
1787 vnodedev_output
+= "\nDevice Specific Information:\t"
1788 if (vnode
.v_type
== vblk_type
):
1789 vnodedev_output
+= "Sorry, I do not know how to decode block devices yet!"
1790 vnodedev_output
+= "\nMaybe you can write me!"
1792 if (vnode
.v_type
== vchr_type
):
1793 # Device information; this is scanty
1795 if (devnode_major
> 42) or (devnode_major
< 0):
1796 vnodedev_output
+= "Invalid major #\n"
1797 # static assignments in conf
1798 elif (devnode_major
== 0):
1799 vnodedev_output
+= "Console mux device\n"
1800 elif (devnode_major
== 2):
1801 vnodedev_output
+= "Current tty alias\n"
1802 elif (devnode_major
== 3):
1803 vnodedev_output
+= "NULL device\n"
1804 elif (devnode_major
== 4):
1805 vnodedev_output
+= "Old pty slave\n"
1806 elif (devnode_major
== 5):
1807 vnodedev_output
+= "Old pty master\n"
1808 elif (devnode_major
== 6):
1809 vnodedev_output
+= "Kernel log\n"
1810 elif (devnode_major
== 12):
1811 vnodedev_output
+= "Memory devices\n"
1812 # Statically linked dynamic assignments
1813 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptmx_open')):
1814 vnodedev_output
+= "Cloning pty master not done\n"
1815 #GetVnodeDevCpty(devnode_major, devnode_minor)
1816 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptsd_open')):
1817 vnodedev_output
+= "Cloning pty slave not done\n"
1818 #GetVnodeDevCpty(devnode_major, devnode_minor)
1820 vnodedev_output
+= "RESERVED SLOT\n"
1822 vnodedev_output
+= "{:#x} is not a device".format(vnode
)
1823 return vnodedev_output
1825 @lldb_command('showvnodedev')
1826 def ShowVnodeDev(cmd_args
=None):
1827 """ Routine to display details of all vnodes of block and character device types
1828 Usage: showvnodedev <address of vnode>
1831 print "No arguments passed"
1832 print ShowVnodeDev
.__doc
__
1834 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
1836 print "unknown arguments:", str(cmd_args
)
1838 print GetVnodeDevInfo(vnode_val
)
1840 # EndMacro: showvnodedev
1842 # Macro: showvnodelocks
1843 def GetVnodeLock(lockf
):
1844 """ Internal function to get information from the given advisory lock
1845 params: lockf - value representing v_lockf member in struct vnode *
1846 return: str - formatted output information for the advisory lock
1848 vnode_lock_output
= ''
1849 lockf_flags
= lockf
.lf_flags
1850 lockf_type
= lockf
.lf_type
1851 if lockf_flags
& 0x20:
1852 vnode_lock_output
+= ("{: <8s}").format('flock')
1853 if lockf_flags
& 0x40:
1854 vnode_lock_output
+= ("{: <8s}").format('posix')
1855 if lockf_flags
& 0x80:
1856 vnode_lock_output
+= ("{: <8s}").format('prov')
1857 if lockf_flags
& 0x10:
1858 vnode_lock_output
+= ("{: <4s}").format('W')
1859 if lockf_flags
& 0x400:
1860 vnode_lock_output
+= ("{: <8s}").format('ofd')
1862 vnode_lock_output
+= ("{: <4s}").format('.')
1864 # POSIX file vs advisory range locks
1865 if lockf_flags
& 0x40:
1866 lockf_proc
= Cast(lockf
.lf_id
, 'proc *')
1867 vnode_lock_output
+= ("PID {: <18d}").format(lockf_proc
.p_pid
)
1869 vnode_lock_output
+= ("ID {: <#019x}").format(int(lockf
.lf_id
))
1873 vnode_lock_output
+= ("{: <12s}").format('shared')
1876 vnode_lock_output
+= ("{: <12s}").format('exclusive')
1879 vnode_lock_output
+= ("{: <12s}").format('unlock')
1881 vnode_lock_output
+= ("{: <12s}").format('unknown')
1883 # start and stop values
1884 vnode_lock_output
+= ("{: #018x} ..").format(lockf
.lf_start
)
1885 vnode_lock_output
+= ("{: #018x}\n").format(lockf
.lf_end
)
1886 return vnode_lock_output
1888 @header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
1889 def GetVnodeLocksSummary(vnode
):
1890 """ Internal function to get summary of advisory locks for the given vnode
1891 params: vnode - value representing the vnode object
1892 return: str - formatted output information for the summary of advisory locks
1896 lockf_list
= vnode
.v_lockf
1897 for lockf_itr
in IterateLinkedList(lockf_list
, 'lf_next'):
1898 out_str
+= ("{: <4s}").format('H')
1899 out_str
+= GetVnodeLock(lockf_itr
)
1900 lockf_blocker
= lockf_itr
.lf_blkhd
.tqh_first
1901 while lockf_blocker
:
1902 out_str
+= ("{: <4s}").format('>')
1903 out_str
+= GetVnodeLock(lockf_blocker
)
1904 lockf_blocker
= lockf_blocker
.lf_block
.tqe_next
1907 @lldb_command('showvnodelocks')
1908 def ShowVnodeLocks(cmd_args
=None):
1909 """ Routine to display list of advisory record locks for the given vnode address
1910 Usage: showvnodelocks <address of vnode>
1913 print "No arguments passed"
1914 print ShowVnodeLocks
.__doc
__
1916 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
1918 print "unknown arguments:", str(cmd_args
)
1920 print GetVnodeLocksSummary
.header
1921 print GetVnodeLocksSummary(vnode_val
)
1923 # EndMacro: showvnodelocks
1925 # Macro: showproclocks
1927 @lldb_command('showproclocks')
1928 def ShowProcLocks(cmd_args
=None):
1929 """ Routine to display list of advisory record locks for the given process
1930 Usage: showproclocks <address of proc>
1933 print "No arguments passed"
1934 print ShowProcLocks
.__doc
__
1936 proc
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
1938 print "unknown arguments:", str(cmd_args
)
1941 proc_filedesc
= proc
.p_fd
1942 fd_lastfile
= proc_filedesc
.fd_lastfile
1943 fd_ofiles
= proc_filedesc
.fd_ofiles
1946 while count
<= fd_lastfile
:
1947 if fd_ofiles
[count
]:
1948 fglob
= fd_ofiles
[count
].f_fglob
1949 fo_type
= fglob
.fg_ops
.fo_type
1951 fg_data
= fglob
.fg_data
1952 fg_vnode
= Cast(fg_data
, 'vnode *')
1953 name
= fg_vnode
.v_name
1954 lockf_itr
= fg_vnode
.v_lockf
1957 print GetVnodeLocksSummary
.header
1959 out_str
+= ("\n( fd {:d}, name ").format(count
)
1961 out_str
+= "(null) )\n"
1963 out_str
+= "{:s} )\n".format(name
)
1965 print GetVnodeLocksSummary(fg_vnode
)
1967 print "\n{0: d} total locks for {1: #018x}".format(seen
, proc
)
1969 # EndMacro: showproclocks
1971 @lldb_type_summary(['vnode_t', 'vnode *'])
1972 @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'))
1973 def GetVnodeSummary(vnode
):
1974 """ Get a summary of important information out of vnode
1977 format_string
= "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <6s} {8: <35s}"
1978 usecount
= int(vnode
.v_usecount
)
1979 iocount
= int(vnode
.v_iocount
)
1980 v_data_ptr
= int(hex(vnode
.v_data
), 16)
1981 vtype
= int(vnode
.v_type
)
1982 vtype_str
= "%d" % vtype
1983 vnode_types
= ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
1984 if vtype
>= 0 and vtype
< len(vnode_types
):
1985 vtype_str
= vnode_types
[vtype
]
1986 parent_ptr
= int(hex(vnode
.v_parent
), 16)
1987 name_ptr
= int(hex(vnode
.v_name
), 16)
1990 name
= str(vnode
.v_name
)
1991 elif int(vnode
.v_tag
) == 16 :
1992 cnode
= Cast(vnode
.v_data
, 'cnode *')
1993 name
= "hfs: %s" % str( Cast(cnode
.c_desc
.cd_nameptr
, 'char *'))
1995 csblob_version
= '-'
1996 if (vtype
== 1) and (vnode
.v_un
.vu_ubcinfo
!= 0):
1997 csblob_version
= '{: <6d}'.format(vnode
.v_un
.vu_ubcinfo
.cs_add_gen
)
1998 # Check to see if vnode is mapped/unmapped
1999 if (vnode
.v_un
.vu_ubcinfo
.ui_flags
& 0x8) != 0:
2003 out_str
+= format_string
.format(vnode
, usecount
, iocount
, v_data_ptr
, vtype_str
, parent_ptr
, mapped
, csblob_version
, name
)
2006 @lldb_command('showallvnodes')
2007 def ShowAllVnodes(cmd_args
=None):
2008 """ Display info about all vnodes
2010 mntlist
= kern
.globals.mountlist
2011 print GetVnodeSummary
.header
2012 for mntval
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
2013 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2014 print GetVnodeSummary(vnodeval
)
2017 @lldb_command('showvnode')
2018 def ShowVnode(cmd_args
=None):
2019 """ Display info about one vnode
2020 usage: showvnode <vnode>
2022 if cmd_args
== None or len(cmd_args
) < 1:
2023 print "Please provide valid vnode argument. Type help showvnode for help."
2025 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0],'vnode *')
2026 print GetVnodeSummary
.header
2027 print GetVnodeSummary(vnodeval
)
2029 @lldb_command('showvolvnodes')
2030 def ShowVolVnodes(cmd_args
=None):
2031 """ Display info about all vnodes of a given mount_t
2033 if cmd_args
== None or len(cmd_args
) < 1:
2034 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2036 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2037 print GetVnodeSummary
.header
2038 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2039 print GetVnodeSummary(vnodeval
)
2042 @lldb_command('showvolbusyvnodes')
2043 def ShowVolBusyVnodes(cmd_args
=None):
2044 """ Display info about busy (iocount!=0) vnodes of a given mount_t
2046 if cmd_args
== None or len(cmd_args
) < 1:
2047 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2049 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2050 print GetVnodeSummary
.header
2051 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2052 if int(vnodeval
.v_iocount
) != 0:
2053 print GetVnodeSummary(vnodeval
)
2055 @lldb_command('showallbusyvnodes')
2056 def ShowAllBusyVnodes(cmd_args
=None):
2057 """ Display info about all busy (iocount!=0) vnodes
2059 mntlistval
= kern
.globals.mountlist
2060 for mntval
in IterateTAILQ_HEAD(mntlistval
, 'mnt_list'):
2061 ShowVolBusyVnodes([hex(mntval
)])
2063 @lldb_command('print_vnode')
2064 def PrintVnode(cmd_args
=None):
2065 """ Prints out the fields of a vnode struct
2066 Usage: print_vnode <vnode>
2069 print "Please provide valid vnode argument. Type help print_vnode for help."
2073 @lldb_command('showworkqvnodes')
2074 def ShowWorkqVnodes(cmd_args
=None):
2075 """ Print the vnode worker list
2076 Usage: showworkqvnodes <struct mount *>
2079 print "Please provide valid mount argument. Type help showworkqvnodes for help."
2082 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2083 vp
= Cast(mp
.mnt_workerqueue
.tqh_first
, 'vnode *')
2084 print GetVnodeSummary
.header
2086 print GetVnodeSummary(vp
)
2087 vp
= vp
.v_mntvnodes
.tqe_next
2089 @lldb_command('shownewvnodes')
2090 def ShowNewVnodes(cmd_args
=None):
2091 """ Print the new vnode list
2092 Usage: shownewvnodes <struct mount *>
2095 print "Please provide valid mount argument. Type help shownewvnodes for help."
2097 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2098 vp
= Cast(mp
.mnt_newvnodes
.tqh_first
, 'vnode *')
2099 print GetVnodeSummary
.header
2101 print GetVnodeSummary(vp
)
2102 vp
= vp
.v_mntvnodes
.tqe_next
2105 @lldb_command('showprocvnodes')
2106 def ShowProcVnodes(cmd_args
=None):
2107 """ Routine to print out all the open fds which are vnodes in a process
2108 Usage: showprocvnodes <proc *>
2111 print "Please provide valid proc argument. Type help showprocvnodes for help."
2113 procptr
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
2114 fdptr
= Cast(procptr
.p_fd
, 'filedesc *')
2115 if int(fdptr
.fd_cdir
) != 0:
2116 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_cdir
))
2117 if int(fdptr
.fd_rdir
) != 0:
2118 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_rdir
))
2120 print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary
.header
2121 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
2122 fpptr
= Cast(fdptr
.fd_ofiles
, 'fileproc *')
2123 while count
< fdptr
.fd_nfiles
:
2124 fpp
= dereference(fpptr
)
2125 fproc
= Cast(fpp
, 'fileproc *')
2127 fglob
= dereference(fproc
).f_fglob
2129 if (int(fglob
) != 0) and (int(fglob
.fg_ops
.fo_type
) == 1):
2130 if (fdptr
.fd_ofileflags
[count
] & 1): flags
+= 'E'
2131 if (fdptr
.fd_ofileflags
[count
] & 2): flags
+= 'F'
2132 if (fdptr
.fd_ofileflags
[count
] & 4): flags
+= 'R'
2133 if (fdptr
.fd_ofileflags
[count
] & 8): flags
+= 'C'
2134 print '{0: <5d} {1: <7s}'.format(count
, flags
) + GetVnodeSummary(Cast(fglob
.fg_data
, 'vnode *'))
2136 fpptr
= kern
.GetValueFromAddress(int(fpptr
) + kern
.ptrsize
,'fileproc *')
2138 @lldb_command('showallprocvnodes')
2139 def ShowAllProcVnodes(cmd_args
=None):
2140 """ Routine to print out all the open fds which are vnodes
2143 procptr
= Cast(kern
.globals.allproc
.lh_first
, 'proc *')
2144 while procptr
and int(procptr
) != 0:
2145 print '{:<s}'.format("=" * 106)
2146 print GetProcInfo(procptr
)
2147 ShowProcVnodes([int(procptr
)])
2148 procptr
= procptr
.p_list
.le_next
2150 @xnudebug_test('test_vnode')
2151 def TestShowAllVnodes(kernel_target
, config
, lldb_obj
, isConnected
):
2152 """ Test the functionality of vnode related commands
2158 print "Target is not connected. Cannot test memstats"
2160 res
= lldb
.SBCommandReturnObject()
2161 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("showallvnodes", res
)
2162 result
= res
.GetOutput()
2163 if len(result
.split("\n")) > 2 and result
.find('VREG') != -1 and len(result
.splitlines()[2].split()) > 5:
2169 @lldb_type_summary(['_lck_grp_ *'])
2170 def GetMutexEntry(mtxg
):
2171 """ Summarize a mutex group entry with important information.
2173 mtxg: value - obj representing a mutex group in kernel
2175 out_string - summary of the mutex group
2179 if kern
.ptrsize
== 8:
2180 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2182 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2184 if mtxg
.lck_grp_mtxcnt
:
2185 out_string
+= format_string
.format(mtxg
, mtxg
.lck_grp_mtxcnt
,mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_util_cnt
,
2186 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_miss_cnt
,
2187 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_wait_cnt
, mtxg
.lck_grp_name
)
2190 @lldb_command('showallmtx')
2191 def ShowAllMtx(cmd_args
=None):
2192 """ Routine to print a summary listing of all mutexes
2195 if kern
.ptrsize
== 8:
2196 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2198 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2200 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2202 mtxgrp_queue_head
= kern
.globals.lck_grp_queue
2203 mtxgrp_ptr_type
= GetType('_lck_grp_ *')
2205 for mtxgrp_ptr
in IterateQueue(mtxgrp_queue_head
, mtxgrp_ptr_type
, "lck_grp_link"):
2206 print GetMutexEntry(mtxgrp_ptr
)
2208 # EndMacro: showallmtx
2210 # Macro: showallrwlck
2211 @lldb_type_summary(['_lck_grp_ *'])
2212 def GetRWLEntry(rwlg
):
2213 """ Summarize a reader writer lock group with important information.
2215 rwlg: value - obj representing a reader writer lock group in kernel
2217 out_string - summary of the reader writer lock group
2221 if kern
.ptrsize
== 8:
2222 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2224 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2226 if rwlg
.lck_grp_rwcnt
:
2227 out_string
+= format_string
.format(rwlg
, rwlg
.lck_grp_rwcnt
,rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_util_cnt
,
2228 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_miss_cnt
,
2229 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_wait_cnt
, rwlg
.lck_grp_name
)
2233 @lldb_type_summary(['lck_mtx_t *'])
2234 @header("===== Mutex Lock Summary =====")
2235 def GetMutexLockSummary(mtx
):
2236 """ Summarize mutex lock with important information.
2238 mtx: value - obj representing a mutex lock in kernel
2240 out_str - summary of the mutex lock
2243 return "Invalid lock value: 0x0"
2245 if kern
.arch
== "x86_64":
2246 out_str
= "Lock Type : MUTEX\n"
2247 if mtx
.lck_mtx_tag
== 0x07ff1007 :
2248 out_str
+= "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx
.lck_mtx_ptr
)
2249 mtx
= Cast(mtx
.lck_mtx_ptr
, 'lck_mtx_t *')
2251 if mtx
.lck_mtx_tag
== 0x07fe2007 :
2252 out_str
+= "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx
.lck_mtx_tag
)
2254 out_str
+= "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx
=mtx
)
2255 out_str
+= "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx
=mtx
)
2256 out_str
+= "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx
=mtx
)
2257 out_str
+= "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx
=mtx
)
2258 out_str
+= "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx
=mtx
)
2259 out_str
+= "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx
=mtx
)
2260 out_str
+= "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx
=mtx
)
2261 out_str
+= "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx
=mtx
)
2262 if mtx
.lck_mtxd_pad32
== 0xFFFFFFFF :
2263 out_str
+= "Canary (valid) : {mtx.lck_mtxd_pad32:#x}\n".format(mtx
=mtx
)
2265 out_str
+= "Canary (INVALID) : {mtx.lck_mtxd_pad32:#x}\n".format(mtx
=mtx
)
2268 out_str
= "Lock Type\t\t: MUTEX\n"
2269 out_str
+= "Owner Thread\t\t: {:#x}".format(mtx
.lck_mtx_data
& ~
0x3)
2270 if (mtx
.lck_mtx_data
& ~
0x3) == 0xfffffff0:
2271 out_str
+= " Held as spinlock"
2272 out_str
+= "\nNumber of Waiters\t: {:d}\n".format(mtx
.lck_mtx_waiters
)
2273 out_str
+= "Flags\t\t\t: "
2274 if mtx
.lck_mtx_data
& 0x1:
2275 out_str
+= "[Interlock Locked] "
2276 if mtx
.lck_mtx_data
& 0x2:
2277 out_str
+= "[Wait Flag]"
2280 @lldb_type_summary(['lck_spin_t *'])
2281 @header("===== SpinLock Summary =====")
2282 def GetSpinLockSummary(spinlock
):
2283 """ Summarize spinlock with important information.
2285 spinlock: value - obj representing a spinlock in kernel
2287 out_str - summary of the spinlock
2290 return "Invalid lock value: 0x0"
2292 out_str
= "Lock Type\t\t: SPINLOCK\n"
2293 if kern
.arch
== "x86_64":
2294 out_str
+= "Interlock\t\t: {:#x}\n".format(spinlock
.interlock
)
2297 lock_data
= spinlock
.hwlock
.lock_data
2299 out_str
+= "Invalid state: interlock is locked but no owner\n"
2301 out_str
+= "Owner Thread\t\t: "
2305 out_str
+= "{:#x}\n".format(lock_data
& ~
0x1)
2306 if (lock_data
& 1) == 0:
2307 out_str
+= "Invalid state: owned but interlock bit is not set\n"
2310 @lldb_command('showlock', 'MS')
2311 def ShowLock(cmd_args
=None, cmd_options
={}):
2312 """ Show info about a lock - its state and owner thread details
2313 Usage: showlock <address of a lock>
2314 -M : to consider <addr> as lck_mtx_t
2315 -S : to consider <addr> as lck_spin_t
2318 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
2322 lock
= kern
.GetValueFromAddress(cmd_args
[0], 'uintptr_t *')
2324 if kern
.arch
== "x86_64" and lock
:
2325 if "-M" in cmd_options
:
2326 lock_mtx
= kern
.GetValueFromAddress(lock
, 'lck_mtx_t *')
2327 summary_str
= GetMutexLockSummary(lock_mtx
)
2328 elif "-S" in cmd_options
:
2329 lock_spin
= kern
.GetValueFromAddress(lock
, 'lck_spin_t *')
2330 summary_str
= GetSpinLockSummary(lock_spin
)
2332 summary_str
= "Please specify supported lock option(-M/-S)"
2338 lock_mtx
= Cast(lock
, 'lck_mtx_t*')
2339 if lock_mtx
.lck_mtx_type
== 0x22:
2340 summary_str
= GetMutexLockSummary(lock_mtx
)
2342 lock_spin
= Cast(lock
, 'lck_spin_t*')
2343 if lock_spin
.type == 0x11:
2344 summary_str
= GetSpinLockSummary(lock_spin
)
2346 if summary_str
== "":
2347 summary_str
= "Lock Type\t\t: INVALID LOCK"
2352 @lldb_command('showallrwlck')
2353 def ShowAllRWLck(cmd_args
=None):
2354 """ Routine to print a summary listing of all read/writer locks
2356 if kern
.ptrsize
== 8:
2357 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2359 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2361 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2363 rwlgrp_queue_head
= kern
.globals.lck_grp_queue
2364 rwlgrp_ptr_type
= GetType('_lck_grp_ *')
2365 for rwlgrp_ptr
in IterateQueue(rwlgrp_queue_head
, rwlgrp_ptr_type
, "lck_grp_link"):
2366 print GetRWLEntry(rwlgrp_ptr
)
2368 # EndMacro: showallrwlck
2370 #Macro: showbootermemorymap
2371 @lldb_command('showbootermemorymap')
2372 def ShowBooterMemoryMap(cmd_args
=None):
2373 """ Prints out the phys memory map from kernelBootArgs
2374 Supported only on x86_64
2376 if kern
.arch
!= 'x86_64':
2377 print "showbootermemorymap not supported on this architecture"
2400 boot_args
= kern
.globals.kernelBootArgs
2401 msize
= boot_args
.MemoryMapDescriptorSize
2402 mcount
= (boot_args
.MemoryMapSize
) / unsigned(msize
)
2404 out_string
+= "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2408 mptr
= kern
.GetValueFromAddress(unsigned(boot_args
.MemoryMap
) + kern
.VM_MIN_KERNEL_ADDRESS
+ unsigned(i
*msize
), 'EfiMemoryRange *')
2409 mtype
= unsigned(mptr
.Type
)
2410 if mtype
in memtype_dict
:
2411 out_string
+= "{0: <12s}".format(memtype_dict
[mtype
])
2413 out_string
+= "{0: <12s}".format("UNKNOWN")
2415 if mptr
.VirtualStart
== 0:
2416 out_string
+= "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, ' '*19, mptr
.Attribute
)
2418 out_string
+= "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, mptr
.VirtualStart
, mptr
.Attribute
)
2422 #EndMacro: showbootermemorymap
2424 @lldb_command('show_all_purgeable_objects')
2425 def ShowAllPurgeableVmObjects(cmd_args
=None):
2426 """ Routine to print a summary listing of all the purgeable vm objects
2428 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2429 ShowAllPurgeableVolatileVmObjects()
2430 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2431 ShowAllPurgeableNonVolatileVmObjects()
2433 @lldb_command('show_all_purgeable_nonvolatile_objects')
2434 def ShowAllPurgeableNonVolatileVmObjects(cmd_args
=None):
2435 """ Routine to print a summary listing of all the vm objects in
2436 the purgeable_nonvolatile_queue
2439 nonvolatile_total
= lambda:None
2440 nonvolatile_total
.objects
= 0
2441 nonvolatile_total
.vsize
= 0
2442 nonvolatile_total
.rsize
= 0
2443 nonvolatile_total
.wsize
= 0
2444 nonvolatile_total
.csize
= 0
2445 nonvolatile_total
.disowned_objects
= 0
2446 nonvolatile_total
.disowned_vsize
= 0
2447 nonvolatile_total
.disowned_rsize
= 0
2448 nonvolatile_total
.disowned_wsize
= 0
2449 nonvolatile_total
.disowned_csize
= 0
2451 queue_len
= kern
.globals.purgeable_nonvolatile_count
2452 queue_head
= kern
.globals.purgeable_nonvolatile_queue
2454 print 'purgeable_nonvolatile_queue:{:#018x} purgeable_volatile_count:{:d}\n'.format(kern
.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len
)
2455 print 'N:non-volatile V:volatile E:empty D:deny\n'
2457 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process")
2459 for object in IterateQueue(queue_head
, 'struct vm_object *', 'objq'):
2461 ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
)
2462 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
)
2463 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
)
2466 def ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
):
2467 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
2469 object - core.value : a object of type 'struct vm_object *'
2473 page_size
= kern
.globals.page_size
2474 if object.purgable
== 0:
2476 elif object.purgable
== 1:
2478 elif object.purgable
== 2:
2480 elif object.purgable
== 3:
2484 if object.pager
== 0:
2485 compressed_count
= 0
2487 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
2488 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
2490 print "{:>6d}/{:<6d} {:#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:#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_un2
.vou_purgeable_owner
,GetProcPIDForTask(object.vo_un2
.vou_purgeable_owner
),GetProcNameForTask(object.vo_un2
.vou_purgeable_owner
))
2492 nonvolatile_total
.objects
+= 1
2493 nonvolatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
2494 nonvolatile_total
.rsize
+= object.resident_page_count
2495 nonvolatile_total
.wsize
+= object.wired_page_count
2496 nonvolatile_total
.csize
+= compressed_count
2497 if object.vo_un2
.vou_purgeable_owner
== 0:
2498 nonvolatile_total
.disowned_objects
+= 1
2499 nonvolatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
2500 nonvolatile_total
.disowned_rsize
+= object.resident_page_count
2501 nonvolatile_total
.disowned_wsize
+= object.wired_page_count
2502 nonvolatile_total
.disowned_csize
+= compressed_count
2505 @lldb_command('show_all_purgeable_volatile_objects')
2506 def ShowAllPurgeableVolatileVmObjects(cmd_args
=None):
2507 """ Routine to print a summary listing of all the vm objects in
2508 the purgeable queues
2510 volatile_total
= lambda:None
2511 volatile_total
.objects
= 0
2512 volatile_total
.vsize
= 0
2513 volatile_total
.rsize
= 0
2514 volatile_total
.wsize
= 0
2515 volatile_total
.csize
= 0
2516 volatile_total
.disowned_objects
= 0
2517 volatile_total
.disowned_vsize
= 0
2518 volatile_total
.disowned_rsize
= 0
2519 volatile_total
.disowned_wsize
= 0
2520 volatile_total
.disowned_csize
= 0
2522 purgeable_queues
= kern
.globals.purgeable_queues
2523 print "---------- OBSOLETE\n"
2524 ShowPurgeableQueue(purgeable_queues
[0], volatile_total
)
2525 print "\n\n---------- FIFO\n"
2526 ShowPurgeableQueue(purgeable_queues
[1], volatile_total
)
2527 print "\n\n---------- LIFO\n"
2528 ShowPurgeableQueue(purgeable_queues
[2], volatile_total
)
2530 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
)
2531 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
)
2532 purgeable_count
= kern
.globals.vm_page_purgeable_count
2533 purgeable_wired_count
= kern
.globals.vm_page_purgeable_wired_count
2534 if purgeable_count
!= volatile_total
.rsize
or purgeable_wired_count
!= volatile_total
.wsize
:
2535 mismatch
= "<--------- MISMATCH\n"
2538 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count
, purgeable_wired_count
, mismatch
)
2541 def ShowPurgeableQueue(qhead
, volatile_total
):
2542 print "----- GROUP 0\n"
2543 ShowPurgeableGroup(qhead
.objq
[0], volatile_total
)
2544 print "----- GROUP 1\n"
2545 ShowPurgeableGroup(qhead
.objq
[1], volatile_total
)
2546 print "----- GROUP 2\n"
2547 ShowPurgeableGroup(qhead
.objq
[2], volatile_total
)
2548 print "----- GROUP 3\n"
2549 ShowPurgeableGroup(qhead
.objq
[3], volatile_total
)
2550 print "----- GROUP 4\n"
2551 ShowPurgeableGroup(qhead
.objq
[4], volatile_total
)
2552 print "----- GROUP 5\n"
2553 ShowPurgeableGroup(qhead
.objq
[5], volatile_total
)
2554 print "----- GROUP 6\n"
2555 ShowPurgeableGroup(qhead
.objq
[6], volatile_total
)
2556 print "----- GROUP 7\n"
2557 ShowPurgeableGroup(qhead
.objq
[7], volatile_total
)
2559 def ShowPurgeableGroup(qhead
, volatile_total
):
2561 for object in IterateQueue(qhead
, 'struct vm_object *', 'objq'):
2563 # 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","")
2564 print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process")
2566 ShowPurgeableVolatileVmObject(object, idx
, volatile_total
)
2568 def ShowPurgeableVolatileVmObject(object, idx
, volatile_total
):
2569 """ Routine to print out a summary a VM object in a purgeable queue
2571 object - core.value : a object of type 'struct vm_object *'
2575 ## if int(object.vo_un2.vou_purgeable_owner) != int(object.vo_purgeable_volatilizer):
2579 page_size
= kern
.globals.page_size
2580 if object.purgable
== 0:
2582 elif object.purgable
== 1:
2584 elif object.purgable
== 2:
2586 elif object.purgable
== 3:
2590 if object.pager
== 0:
2591 compressed_count
= 0
2593 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
2594 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
2595 # 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_purgeable_owner,GetProcPIDForTask(object.vo_un2.vou_purgeable_owner),GetProcNameForTask(object.vo_un2.vou_purgeable_owner),object.vo_purgeable_volatilizer,GetProcPIDForTask(object.vo_purgeable_volatilizer),GetProcNameForTask(object.vo_purgeable_volatilizer),diff)
2596 print "{:>6d} {:#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:#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_un2
.vou_purgeable_owner
,GetProcPIDForTask(object.vo_un2
.vou_purgeable_owner
),GetProcNameForTask(object.vo_un2
.vou_purgeable_owner
))
2597 volatile_total
.objects
+= 1
2598 volatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
2599 volatile_total
.rsize
+= object.resident_page_count
2600 volatile_total
.wsize
+= object.wired_page_count
2601 volatile_total
.csize
+= compressed_count
2602 if object.vo_un2
.vou_purgeable_owner
== 0:
2603 volatile_total
.disowned_objects
+= 1
2604 volatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
2605 volatile_total
.disowned_rsize
+= object.resident_page_count
2606 volatile_total
.disowned_wsize
+= object.wired_page_count
2607 volatile_total
.disowned_csize
+= compressed_count
2610 def GetCompressedPagesForObject(obj
):
2613 pager
= Cast(obj
.pager
, 'compressor_pager_t')
2614 return pager
.cpgr_num_slots_occupied
2615 """ # commented code below
2616 if pager.cpgr_num_slots > 128:
2617 slots_arr = pager.cpgr_slots.cpgr_islots
2618 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
2621 compressed_pages = 0
2622 while index < num_indirect_slot_ptr:
2624 if slots_arr[index]:
2625 while compressor_slot < 128:
2626 if slots_arr[index][compressor_slot]:
2627 compressed_pages += 1
2628 compressor_slot += 1
2631 slots_arr = pager.cpgr_slots.cpgr_dslots
2633 compressed_pages = 0
2634 while compressor_slot < pager.cpgr_num_slots:
2635 if slots_arr[compressor_slot]:
2636 compressed_pages += 1
2637 compressor_slot += 1
2638 return compressed_pages
2641 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
2642 """ Routine to print out a summary listing of all the entries in a vm_map
2644 task - core.value : a object of type 'task *'
2648 print "vm_map entries for task " + hex(task
)
2649 print GetTaskSummary
.header
2650 print GetTaskSummary(task
)
2652 print "Task {0: <#020x} has map = 0x0"
2654 showmapvme(task
.map, show_pager_info
, show_all_shadows
)
2656 @lldb_command("showmapvme", "PS")
2657 def ShowMapVME(cmd_args
=None, cmd_options
={}):
2658 """Routine to print out info about the specified vm_map and its vm entries
2659 usage: showmapvme <vm_map>
2660 Use -S flag to show VM object shadow chains
2661 Use -P flag to show pager info (mapped file, compressed pages, ...)
2663 if cmd_args
== None or len(cmd_args
) < 1:
2664 print "Invalid argument.", ShowMap
.__doc
__
2666 show_pager_info
= False
2667 show_all_shadows
= False
2668 if "-P" in cmd_options
:
2669 show_pager_info
= True
2670 if "-S" in cmd_options
:
2671 show_all_shadows
= True
2672 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
2673 showmapvme(map, show_pager_info
, show_all_shadows
)
2675 def showmapvme(map, show_pager_info
, show_all_shadows
):
2676 page_size
= kern
.globals.page_size
2677 vnode_pager_ops
= kern
.globals.vnode_pager_ops
2678 vnode_pager_ops_addr
= unsigned(addressof(vnode_pager_ops
))
2681 rsize
= int(map.pmap
.stats
.resident_count
)
2682 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
2683 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
)
2684 vme_list_head
= map.hdr
.links
2685 vme_ptr_type
= GetType('vm_map_entry *')
2686 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<10s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
2687 last_end
= unsigned(map.hdr
.links
.start
)
2688 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
2689 if unsigned(vme
.links
.start
) != last_end
:
2690 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,vme
.links
.start
,(unsigned(vme
.links
.start
) - last_end
)/page_size
)
2691 last_end
= unsigned(vme
.links
.end
)
2692 size
= unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)
2693 object = vme
.vme_object
.vmo_object
2695 object_str
= "{:<#018x}".format(object)
2696 elif vme
.is_sub_map
:
2697 if object == kern
.globals.bufferhdr_map
:
2698 object_str
= "BUFFERHDR_MAP"
2699 elif object == kern
.globals.mb_map
:
2700 object_str
= "MB_MAP"
2701 elif object == kern
.globals.bsd_pageable_map
:
2702 object_str
= "BSD_PAGEABLE_MAP"
2703 elif object == kern
.globals.ipc_kernel_map
:
2704 object_str
= "IPC_KERNEL_MAP"
2705 elif object == kern
.globals.ipc_kernel_copy_map
:
2706 object_str
= "IPC_KERNEL_COPY_MAP"
2707 elif object == kern
.globals.kalloc_map
:
2708 object_str
= "KALLOC_MAP"
2709 elif object == kern
.globals.zone_map
:
2710 object_str
= "ZONE_MAP"
2711 elif hasattr(kern
.globals, 'compressor_map') and object == kern
.globals.compressor_map
:
2712 object_str
= "COMPRESSOR_MAP"
2713 elif hasattr(kern
.globals, 'gzalloc_map') and object == kern
.globals.gzalloc_map
:
2714 object_str
= "GZALLOC_MAP"
2715 elif hasattr(kern
.globals, 'g_kext_map') and object == kern
.globals.g_kext_map
:
2716 object_str
= "G_KEXT_MAP"
2717 elif hasattr(kern
.globals, 'vector_upl_submap') and object == kern
.globals.vector_upl_submap
:
2718 object_str
= "VECTOR_UPL_SUBMAP"
2720 object_str
= "submap:{:<#018x}".format(object)
2722 if object == kern
.globals.kernel_object
:
2723 object_str
= "KERNEL_OBJECT"
2724 elif object == kern
.globals.vm_submap_object
:
2725 object_str
= "VM_SUBMAP_OBJECT"
2726 elif object == kern
.globals.compressor_object
:
2727 object_str
= "COMPRESSOR_OBJECT"
2729 object_str
= "{:<#018x}".format(object)
2730 offset
= unsigned(vme
.vme_offset
) & ~
0xFFF
2731 tag
= unsigned(vme
.vme_offset
& 0xFFF)
2737 if vme
.is_sub_map
and vme
.use_pmap
:
2740 if map.pmap
== kern
.globals.kernel_pmap
:
2741 xsite
= Cast(kern
.globals.vm_allocation_sites
[tag
],'OSKextAccount *')
2742 if xsite
and xsite
.site
.flags
& 0x0200:
2743 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
2744 print "{:#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:1d}{:1d}{:<8s} {:<18s} {:<#18x}".format(vme
,vme
.links
.start
,vme
.links
.end
,(unsigned(vme
.links
.end
)-unsigned(vme
.links
.start
))/page_size
,tag
,tagstr
,vme
.protection
,vme
.max_protection
,vme_flags
,object_str
,offset
)
2745 if (show_pager_info
or show_all_shadows
) and vme
.is_sub_map
== 0 and vme
.vme_object
.vmo_object
!= 0:
2746 object = vme
.vme_object
.vmo_object
2752 if show_all_shadows
== False and depth
!= 1 and object.shadow
!= 0:
2753 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
2754 object = object.shadow
2756 if object.copy_strategy
== 0:
2758 elif object.copy_strategy
== 2:
2760 elif object.copy_strategy
== 4:
2763 copy_strategy
=str(object.copy_strategy
)
2765 internal
= "internal"
2767 internal
= "external"
2769 pager
= object.pager
2770 if show_pager_info
and pager
!= 0:
2772 pager_string
= "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
2773 elif unsigned(pager
.mo_pager_ops
) == vnode_pager_ops_addr
:
2774 vnode_pager
= Cast(pager
,'vnode_pager *')
2775 pager_string
= "-> " + GetVnodePath(vnode_pager
.vnode_handle
)
2777 pager_string
= "-> {:s}:{:#018x}".format(pager
.mo_pager_ops
.memory_object_pager_name
, pager
.mo_pager_ops
)
2778 print "{:>18d} {:#018x}:{:#018x} {:#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} ({:d} {:d} {:d}) {:s}".format(depth
,offset
,offset
+size
,object,object.ref_count
,object.true_share
,copy_strategy
,internal
,unsigned(object.vo_un1
.vou_size
)/page_size
,object.resident_page_count
,object.wired_page_count
,pager_string
)
2779 # 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)
2780 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
2781 object = object.shadow
2782 if unsigned(map.hdr
.links
.end
) > last_end
:
2783 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,map.hdr
.links
.end
,(unsigned(map.hdr
.links
.end
) - last_end
)/page_size
)
2786 def CountMapTags(map, tagcounts
, slow
):
2787 page_size
= unsigned(kern
.globals.page_size
)
2788 vme_list_head
= map.hdr
.links
2789 vme_ptr_type
= GetType('vm_map_entry *')
2790 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
2791 object = vme
.vme_object
.vmo_object
2792 tag
= vme
.vme_offset
& 0xFFF
2793 if object == kern
.globals.kernel_object
:
2796 count
= unsigned(vme
.links
.end
- vme
.links
.start
) / page_size
2798 addr
= unsigned(vme
.links
.start
)
2799 while addr
< unsigned(vme
.links
.end
):
2800 hash_id
= _calc_vm_page_hash(object, addr
)
2801 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
2802 page
= _vm_page_unpack_ptr(page_list
)
2804 vmpage
= kern
.GetValueFromAddress(page
, 'vm_page_t')
2805 if (addr
== unsigned(vmpage
.offset
)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage
.vm_page_object
))):
2806 if (not vmpage
.local
) and (vmpage
.wire_count
> 0):
2809 page
= _vm_page_unpack_ptr(vmpage
.next_m
)
2811 tagcounts
[tag
] += count
2812 elif vme
.is_sub_map
:
2813 CountMapTags(Cast(object,'vm_map_t'), tagcounts
, slow
)
2816 def CountWiredObject(object, tagcounts
):
2817 tagcounts
[unsigned(object.wire_tag
)] += object.wired_page_count
2820 def CountWiredPurgeableGroup(qhead
, tagcounts
):
2821 for object in IterateQueue(qhead
, 'struct vm_object *', 'objq'):
2822 CountWiredObject(object, tagcounts
)
2825 def CountWiredPurgeableQueue(qhead
, tagcounts
):
2826 CountWiredPurgeableGroup(qhead
.objq
[0], tagcounts
)
2827 CountWiredPurgeableGroup(qhead
.objq
[1], tagcounts
)
2828 CountWiredPurgeableGroup(qhead
.objq
[2], tagcounts
)
2829 CountWiredPurgeableGroup(qhead
.objq
[3], tagcounts
)
2830 CountWiredPurgeableGroup(qhead
.objq
[4], tagcounts
)
2831 CountWiredPurgeableGroup(qhead
.objq
[5], tagcounts
)
2832 CountWiredPurgeableGroup(qhead
.objq
[6], tagcounts
)
2833 CountWiredPurgeableGroup(qhead
.objq
[7], tagcounts
)
2835 def GetKmodIDName(kmod_id
):
2836 kmod_val
= kern
.globals.kmod
2837 for kmod
in IterateLinkedList(kmod_val
, 'next'):
2838 if (kmod
.id == kmod_id
):
2839 return "{:<50s}".format(kmod
.name
)
2843 0: "VM_KERN_MEMORY_NONE",
2844 1: "VM_KERN_MEMORY_OSFMK",
2845 2: "VM_KERN_MEMORY_BSD",
2846 3: "VM_KERN_MEMORY_IOKIT",
2847 4: "VM_KERN_MEMORY_LIBKERN",
2848 5: "VM_KERN_MEMORY_OSKEXT",
2849 6: "VM_KERN_MEMORY_KEXT",
2850 7: "VM_KERN_MEMORY_IPC",
2851 8: "VM_KERN_MEMORY_STACK",
2852 9: "VM_KERN_MEMORY_CPU",
2853 10: "VM_KERN_MEMORY_PMAP",
2854 11: "VM_KERN_MEMORY_PTE",
2855 12: "VM_KERN_MEMORY_ZONE",
2856 13: "VM_KERN_MEMORY_KALLOC",
2857 14: "VM_KERN_MEMORY_COMPRESSOR",
2858 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
2859 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
2860 17: "VM_KERN_MEMORY_WAITQ",
2861 18: "VM_KERN_MEMORY_DIAG",
2862 19: "VM_KERN_MEMORY_LOG",
2863 20: "VM_KERN_MEMORY_FILE",
2864 21: "VM_KERN_MEMORY_MBUF",
2865 22: "VM_KERN_MEMORY_UBC",
2866 23: "VM_KERN_MEMORY_SECURITY",
2867 24: "VM_KERN_MEMORY_MLOCK",
2868 25: "VM_KERN_MEMORY_REASON",
2869 26: "VM_KERN_MEMORY_SKYWALK",
2870 27: "VM_KERN_MEMORY_LTABLE",
2871 255:"VM_KERN_MEMORY_ANY",
2874 def GetVMKernName(tag
):
2875 return FixedTags
[tag
]
2877 @lldb_command("showvmtags", "S")
2878 def showvmtags(cmd_args
=None, cmd_options
={}):
2879 """Routine to print out info about kernel wired page allocations
2881 iterates kernel map and vm objects totaling allocations by tag.
2882 usage: showvmtags -S
2883 also iterates kernel object pages individually - slow.
2886 if "-S" in cmd_options
:
2888 page_size
= unsigned(kern
.globals.page_size
)
2891 for tag
in range(256):
2893 for tag
in range(256):
2896 if kern
.globals.vm_tag_active_update
:
2897 for tag
in range(256):
2898 site
= kern
.globals.vm_allocation_sites
[tag
]
2900 tagcounts
[unsigned(tag
)] = unsigned(site
.total
)
2901 tagpeaks
[unsigned(tag
)] = unsigned(site
.peak
)
2903 queue_head
= kern
.globals.vm_objects_wired
2904 for object in IterateQueue(queue_head
, 'struct vm_object *', 'objq'):
2905 if object != kern
.globals.kernel_object
:
2906 CountWiredObject(object, tagcounts
)
2908 queue_head
= kern
.globals.purgeable_nonvolatile_queue
2909 for object in IterateQueue(queue_head
, 'struct vm_object *', 'objq'):
2910 CountWiredObject(object, tagcounts
)
2912 purgeable_queues
= kern
.globals.purgeable_queues
2913 CountWiredPurgeableQueue(purgeable_queues
[0], tagcounts
)
2914 CountWiredPurgeableQueue(purgeable_queues
[1], tagcounts
)
2915 CountWiredPurgeableQueue(purgeable_queues
[2], tagcounts
)
2917 CountMapTags(kern
.globals.kernel_map
, tagcounts
, slow
)
2920 print " {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod","peak","size","name")
2921 for tag
in range(256):
2923 total
+= tagcounts
[tag
]
2926 if ((tag
<= 27) or (tag
== 255)):
2927 sitestr
= GetVMKernName(tag
)
2929 site
= kern
.globals.vm_allocation_sites
[tag
]
2931 if site
.flags
& 0x007F:
2932 cstr
= addressof(site
.subtotals
[site
.subtotalscount
])
2933 sitestr
= "{:<50s}".format(str(Cast(cstr
, 'char *')))
2935 if site
.flags
& 0x0200:
2936 xsite
= Cast(site
,'OSKextAccount *')
2937 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
2938 sitestr
= GetKmodIDName(xsite
.loadTag
)
2940 sitestr
= kern
.Symbolicate(site
)
2941 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag
,tagstr
,tagpeaks
[tag
] / 1024, tagcounts
[tag
] / 1024,sitestr
)
2942 print "Total: {:>7d}K".format(total
/ 1024)
2946 def FindVMEntriesForVnode(task
, vn
):
2947 """ returns an array of vme that have the vnode set to defined vnode
2948 each entry in array is of format (vme, start_addr, end_address, protection)
2953 pager_ops_addr
= unsigned(addressof(kern
.globals.vnode_pager_ops
))
2954 debuglog("pager_ops_addr %s" % hex(pager_ops_addr
))
2956 if unsigned(pmap
) == 0:
2958 vme_list_head
= vmmap
.hdr
.links
2959 vme_ptr_type
= gettype('vm_map_entry *')
2960 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, 'links'):
2962 if unsigned(vme
.is_sub_map
) == 0 and unsigned(vme
.vme_object
.vmo_object
) != 0:
2963 obj
= vme
.vme_object
.vmo_object
2972 vn_pager
= Cast(obj
.pager
, 'vnode_pager *')
2973 if unsigned(vn_pager
.vn_pgr_hdr
.mo_pager_ops
) == pager_ops_addr
and unsigned(vn_pager
.vnode_handle
) == unsigned(vn
):
2974 retval
.append((vme
, unsigned(vme
.links
.start
), unsigned(vme
.links
.end
), unsigned(vme
.protection
)))
2978 @lldb_command('showtaskloadinfo')
2979 def ShowTaskLoadInfo(cmd_args
=None, cmd_options
={}):
2980 """ Print the load address and uuid for the process
2981 Usage: (lldb)showtaskloadinfo <task_t>
2984 raise ArgumentError("Insufficient arguments")
2985 t
= kern
.GetValueFromAddress(cmd_args
[0], 'struct task *')
2986 print_format
= "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
2987 p
= Cast(t
.bsd_info
, 'struct proc *')
2989 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
)
2990 filepath
= GetVnodePath(p
.p_textvp
)
2991 libname
= filepath
.split('/')[-1]
2992 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
2993 mappings
= FindVMEntriesForVnode(t
, p
.p_textvp
)
3000 #print "Load address: %s" % hex(m[1])
3001 print print_format
.format(load_addr
, end_addr
, libname
, uuid_out_string
, filepath
)
3004 @header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3005 @lldb_command('vmpagelookup')
3006 def VMPageLookup(cmd_args
=None):
3007 """ Print the pages in the page bucket corresponding to the provided object and offset.
3008 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3010 if cmd_args
== None or len(cmd_args
) < 2:
3011 raise ArgumentError("Please specify an object and offset.")
3012 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3014 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3015 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3017 hash_id
= _calc_vm_page_hash(obj
, off
)
3019 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3020 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(page_list
)))
3022 print VMPageLookup
.header
3023 page
= _vm_page_unpack_ptr(page_list
)
3025 pg_t
= kern
.GetValueFromAddress(page
, 'vm_page_t')
3026 print format_string
.format(page
, pg_t
.offset
, _vm_page_unpack_ptr(pg_t
.vm_page_object
))
3027 page
= _vm_page_unpack_ptr(pg_t
.next_m
)
3031 @lldb_command('vmpage_get_phys_page')
3032 def VmPageGetPhysPage(cmd_args
=None):
3033 """ return the physical page for a vm_page_t
3034 usage: vm_page_get_phys_page <vm_page_t>
3036 if cmd_args
== None or len(cmd_args
) < 1:
3037 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
3040 page
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_page_t')
3041 phys_page
= _vm_page_get_phys_page(page
)
3042 print("phys_page = 0x%x\n" % phys_page
)
3045 def _vm_page_get_phys_page(page
):
3046 if kern
.arch
== 'x86_64':
3047 return page
.phys_page
3053 if m
>= unsigned(kern
.globals.vm_page_array_beginning_addr
) and m
< unsigned(kern
.globals.vm_page_array_ending_addr
) :
3054 return (m
- unsigned(kern
.globals.vm_page_array_beginning_addr
)) / sizeof('struct vm_page') + unsigned(kern
.globals.vm_first_phys_ppnum
)
3056 page_with_ppnum
= Cast(page
, 'uint32_t *')
3057 ppnum_offset
= sizeof('struct vm_page') / sizeof('uint32_t')
3058 return page_with_ppnum
[ppnum_offset
]
3061 @lldb_command('vmpage_unpack_ptr')
3062 def VmPageUnpackPtr(cmd_args
=None):
3063 """ unpack a pointer
3064 usage: vm_page_unpack_ptr <packed_ptr>
3066 if cmd_args
== None or len(cmd_args
) < 1:
3067 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
3070 packed
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long')
3071 unpacked
= _vm_page_unpack_ptr(packed
)
3072 print("unpacked pointer = 0x%x\n" % unpacked
)
3075 def _vm_page_unpack_ptr(page
):
3076 if kern
.ptrsize
== 4 :
3082 min_addr
= kern
.globals.vm_min_kernel_and_kext_address
3083 ptr_shift
= kern
.globals.vm_packed_pointer_shift
3084 ptr_mask
= kern
.globals.vm_packed_from_vm_pages_array_mask
3085 #INTEL - min_addr = 0xffffff7f80000000
3086 #ARM - min_addr = 0x80000000
3087 #ARM64 - min_addr = 0xffffff8000000000
3088 if unsigned(page
) & unsigned(ptr_mask
) :
3089 masked_page
= (unsigned(page
) & ~ptr_mask
)
3090 return (unsigned(addressof(kern
.globals.vm_pages
[masked_page
])))
3091 return ((unsigned(page
) << unsigned(ptr_shift
)) + unsigned(min_addr
))
3093 @lldb_command('calcvmpagehash')
3094 def CalcVMPageHash(cmd_args
=None):
3095 """ Get the page bucket corresponding to the provided object and offset.
3096 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3098 if cmd_args
== None or len(cmd_args
) < 2:
3099 raise ArgumentError("Please specify an object and offset.")
3101 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3102 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3104 hash_id
= _calc_vm_page_hash(obj
, off
)
3106 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(kern
.globals.vm_page_buckets
[hash_id
].page_list
)))
3109 def _calc_vm_page_hash(obj
, off
):
3110 bucket_hash
= (int) (kern
.globals.vm_page_bucket_hash
)
3111 hash_mask
= (int) (kern
.globals.vm_page_hash_mask
)
3113 one
= (obj
* bucket_hash
) & 0xFFFFFFFF
3114 two
= off
>> unsigned(kern
.globals.page_shift
)
3115 three
= two ^ bucket_hash
3117 hash_id
= four
& hash_mask
3121 VM_PAGE_IS_WIRED
= 1
3123 @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"))
3124 @lldb_command('vmobjectwalkpages', 'SBNQP:')
3125 def VMObjectWalkPages(cmd_args
=None, cmd_options
={}):
3126 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
3127 specifically look for this page, highlighting it in the output or noting if it was not found. For
3128 each page, we confirm that it points to the object. We also keep track of the number of pages we
3129 see and compare this to the object's resident page count field.
3131 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
3132 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
3133 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
3134 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)
3135 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
3136 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
3140 if (cmd_args
== None or len(cmd_args
) < 1):
3141 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
3145 obj
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
3148 if "-P" in cmd_options
:
3149 page
= kern
.GetValueFromAddress(cmd_options
['-P'], 'vm_page_t')
3152 if "-S" in cmd_options
:
3154 raise ArgumentError("-S can only be passed when a page is specified with -P")
3157 walk_backwards
= False
3158 if "-B" in cmd_options
:
3159 walk_backwards
= True
3162 if "-Q" in cmd_options
:
3166 print VMObjectWalkPages
.header
3167 format_string
= "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
3168 first_bitfield_format_string
= "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
3169 second_bitfield_format_string
= "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
3170 second_bitfield_format_string
+= "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
3171 second_bitfield_format_string
+= "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
3172 second_bitfield_format_string
+= "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
3174 limit
= 4096 #arbitrary limit of number of pages to walk
3176 if "-N" in cmd_options
:
3180 res_page_count
= unsigned(obj
.resident_page_count
)
3184 for vmp
in IterateQueue(obj
.memq
, "vm_page_t", "listq", walk_backwards
, unpack_ptr_fn
=_vm_page_unpack_ptr
):
3187 if (page
!= 0 and not(page_found
) and vmp
== page
):
3188 out_string
+= "******"
3191 if page
!= 0 or quiet_mode
:
3192 if (page_count
% 1000) == 0:
3193 print "traversed %d pages ...\n" % (page_count
)
3195 out_string
+= format_string
.format(page_count
, res_page_count
, vmp
, vmp
.offset
, _vm_page_unpack_ptr(vmp
.listq
.next
), _vm_page_get_phys_page(vmp
), vmp
.wire_count
)
3196 out_string
+= first_bitfield_format_string
.format(vmp
.vm_page_q_state
, vmp
.vm_page_in_background
, vmp
.vm_page_on_backgroundq
, vmp
.gobbled
, vmp
.laundry
, vmp
.no_cache
,
3197 vmp
.private
, vmp
.reference
)
3199 out_string
+= second_bitfield_format_string
.format(vmp
.busy
, vmp
.wanted
, vmp
.tabled
, vmp
.hashed
, vmp
.fictitious
, vmp
.clustered
,
3200 vmp
.pmapped
, vmp
.xpmapped
, vmp
.wpmapped
, vmp
.free_when_done
, vmp
.absent
,
3201 vmp
.error
, vmp
.dirty
, vmp
.cleaning
, vmp
.precious
, vmp
.overwriting
,
3202 vmp
.restart
, vmp
.unusual
, 0, 0,
3203 vmp
.cs_validated
, vmp
.cs_tainted
, vmp
.cs_nx
, vmp
.reusable
, vmp
.lopage
, vmp
.slid
,
3204 vmp
.written_by_kernel
)
3206 if (vmp
in pages_seen
):
3207 print out_string
+ "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " twice. stopping...\n"
3210 if (_vm_page_unpack_ptr(vmp
.vm_page_object
) != unsigned(obj
)):
3211 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
.vm_page_object
)))
3214 if (vmp
.vm_page_q_state
== VM_PAGE_IS_WIRED
) and (vmp
.wire_count
== 0):
3215 print out_string
+ " page in wired state with wire_count of 0\n"
3216 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + "\n"
3217 print "stopping...\n"
3220 if ((vmp
.__unused
_pageq
_bits
!= 0) or (vmp
.__unused
_object
_bits
!= 0)):
3221 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
.__unused
_pageq
_bits
,
3222 vmp
.__unused
_object
_bits
)
3223 print "stopping...\n"
3229 hash_id
= _calc_vm_page_hash(obj
, vmp
.offset
)
3230 hash_page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3231 hash_page
= _vm_page_unpack_ptr(hash_page_list
)
3234 while (hash_page
!= 0):
3235 hash_page_t
= kern
.GetValueFromAddress(hash_page
, 'vm_page_t')
3236 if hash_page_t
== vmp
:
3238 hash_page
= _vm_page_unpack_ptr(hash_page_t
.next_m
)
3240 if (unsigned(vmp
) != unsigned(hash_page_t
)):
3241 print out_string
+ "unable to find page: " + "{0: <#020x}".format(unsigned(vmp
)) + " from object in kernel page bucket list\n"
3242 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args
[0], unsigned(vmp
.offset
)))
3245 if (page_count
>= limit
and not(ignore_limit
)):
3246 print out_string
+ "Limit reached (%d pages), stopping..." % (limit
)
3251 if page_found
and stop
:
3252 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
)))
3256 print("page found? : %s\n" % page_found
)
3258 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
)))
3261 @lldb_command("show_all_apple_protect_pagers")
3262 def ShowAllAppleProtectPagers(cmd_args
=None):
3263 """Routine to print all apple_protect pagers
3264 usage: show_all_apple_protect_pagers
3266 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")
3267 qhead
= kern
.globals.apple_protect_pager_queue
3268 qtype
= GetType('apple_protect_pager *')
3269 qcnt
= kern
.globals.apple_protect_pager_count
3271 for pager
in IterateQueue(qhead
, qtype
, "pager_queue"):
3273 show_apple_protect_pager(pager
, qcnt
, idx
)
3275 @lldb_command("show_apple_protect_pager")
3276 def ShowAppleProtectPager(cmd_args
=None):
3277 """Routine to print out info about an apple_protect pager
3278 usage: show_apple_protect_pager <pager>
3280 if cmd_args
== None or len(cmd_args
) < 1:
3281 print "Invalid argument.", ShowMap
.__doc
__
3283 pager
= kern
.GetValueFromAddress(cmd_ars
[0], 'apple_protect_pager_t')
3284 show_apple_protect_pager(pager
, 1, 1)
3286 def show_apple_protect_pager(pager
, qcnt
, idx
):
3287 object = pager
.backing_object
3288 shadow
= object.shadow
3291 shadow
= object.shadow
3292 vnode_pager
= Cast(object.pager
,'vnode_pager *')
3293 filename
= GetVnodePath(vnode_pager
.vnode_handle
)
3294 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
)
3296 @lldb_command("show_console_ring")
3297 def ShowConsoleRingData(cmd_args
=None):
3298 """ Print console ring buffer stats and data
3300 cr
= kern
.globals.console_ring
3301 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
)
3303 for i
in range(unsigned(cr
.used
)):
3304 idx
= ((unsigned(cr
.read_ptr
) - unsigned(cr
.buffer)) + i
) % unsigned(cr
.len)
3305 pending_data
.append("{:c}".format(cr
.buffer[idx
]))
3309 print "".join(pending_data
)
3311 # Macro: showjetsamsnapshot
3313 @lldb_command("showjetsamsnapshot", "DA")
3314 def ShowJetsamSnapshot(cmd_args
=None, cmd_options
={}):
3315 """ Dump entries in the jetsam snapshot table
3316 usage: showjetsamsnapshot [-D] [-A]
3317 Use -D flag to print extra physfootprint details
3318 Use -A flag to print all entries (regardless of valid count)
3321 # Not shown are uuid, user_data, cpu_time
3325 show_footprint_details
= False
3326 show_all_entries
= False
3328 if "-D" in cmd_options
:
3329 show_footprint_details
= True
3331 if "-A" in cmd_options
:
3332 show_all_entries
= True
3334 valid_count
= kern
.globals.memorystatus_jetsam_snapshot_count
3335 max_count
= kern
.globals.memorystatus_jetsam_snapshot_max
3337 if (show_all_entries
== True):
3342 print "{:s}".format(valid_count
)
3343 print "{:s}".format(max_count
)
3346 print "The jetsam snapshot is empty."
3347 print "Use -A to force dump all entries (regardless of valid count)"
3350 # Dumps the snapshot header info
3351 print lldb_run_command('p *memorystatus_jetsam_snapshot')
3353 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} {16: >15s}"
3354 if (show_footprint_details
== True):
3355 hdr_format
+= "{17: >15s} {18: >15s} {19: >12s} {20: >12s} {21: >17s} {22: >10s} {23: >13s} {24: >10s}"
3358 if (show_footprint_details
== False):
3359 print hdr_format
.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'max', 'purgeable', 'lifetimeMax')
3360 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)')
3362 print hdr_format
.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'max', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
3363 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
3366 entry_format
= "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
3367 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
3368 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
3369 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} {e.max_pages: >15d} "\
3370 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
3372 if (show_footprint_details
== True):
3373 entry_format
+= "{e.jse_internal_pages: >15d} "\
3374 "{e.jse_internal_compressed_pages: >15d} "\
3375 "{e.jse_iokit_mapped_pages: >12d} "\
3376 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
3377 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
3378 "{e.jse_alternate_accounting_pages: >10d} "\
3379 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
3380 "{e.jse_page_table_pages: >10d}"
3382 snapshot_list
= kern
.globals.memorystatus_jetsam_snapshot
.entries
3385 current_entry
= Cast(snapshot_list
[idx
], 'jetsam_snapshot_entry')
3386 print entry_format
.format(index
=idx
, e
=current_entry
)
3390 # EndMacro: showjetsamsnapshot
3392 # Macro: showvnodecleanblk/showvnodedirtyblk
3394 def _GetBufSummary(buf
):
3395 """ Get a summary of important information out of a buf_t.
3397 initial
= "(struct buf) {0: <#0x} ="
3399 # List all of the fields in this buf summary.
3400 entries
= [buf
.b_hash
, buf
.b_vnbufs
, buf
.b_freelist
, buf
.b_timestamp
, buf
.b_whichq
,
3401 buf
.b_flags
, buf
.b_lflags
, buf
.b_error
, buf
.b_bufsize
, buf
.b_bcount
, buf
.b_resid
,
3402 buf
.b_dev
, buf
.b_datap
, buf
.b_lblkno
, buf
.b_blkno
, buf
.b_iodone
, buf
.b_vp
,
3403 buf
.b_rcred
, buf
.b_wcred
, buf
.b_upl
, buf
.b_real_bp
, buf
.b_act
, buf
.b_drvdata
,
3404 buf
.b_fsprivate
, buf
.b_transaction
, buf
.b_dirtyoff
, buf
.b_dirtyend
, buf
.b_validoff
,
3405 buf
.b_validend
, buf
.b_redundancy_flags
, buf
.b_proc
, buf
.b_attr
]
3407 # Join an (already decent) string representation of each field
3408 # with newlines and indent the region.
3409 joined_strs
= "\n".join([str(i
).rstrip() for i
in entries
]).replace('\n', "\n ")
3411 # Add the total string representation to our title and return it.
3412 out_str
= initial
.format(int(buf
)) + " {\n " + joined_strs + "\n}\n\n"
3415 def _ShowVnodeBlocks(dirty
=True, cmd_args
=None):
3416 """ Display info about all [dirty|clean] blocks in a vnode.
3418 if cmd_args
== None or len(cmd_args
) < 1:
3419 print "Please provide a valid vnode argument."
3422 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
3423 list_head
= vnodeval
.v_cleanblkhd
;
3425 list_head
= vnodeval
.v_dirtyblkhd
3427 print "Blocklist for vnode {}:".format(cmd_args
[0])
3430 for buf
in IterateListEntry(list_head
, 'struct buf *', 'b_hash'):
3431 # For each block (buf_t) in the appropriate list,
3432 # ask for a summary and print it.
3433 print "---->\nblock {}: ".format(i
) + _GetBufSummary(buf
)
3437 @lldb_command('showvnodecleanblk')
3438 def ShowVnodeCleanBlocks(cmd_args
=None):
3439 """ Display info about all clean blocks in a vnode.
3440 usage: showvnodecleanblk <address of vnode>
3442 _ShowVnodeBlocks(False, cmd_args
)
3444 @lldb_command('showvnodedirtyblk')
3445 def ShowVnodeDirtyBlocks(cmd_args
=None):
3446 """ Display info about all dirty blocks in a vnode.
3447 usage: showvnodedirtyblk <address of vnode>
3449 _ShowVnodeBlocks(True, cmd_args
)
3451 # EndMacro: showvnodecleanblk/showvnodedirtyblk