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.
13 from ctypes
import c_int64
15 def vm_unpack_pointer(packed
, params
, type_str
= 'void *'):
16 """ Unpack a pointer packed with VM_PACK_POINTER()
18 packed - value : The packed pointer value
19 params - value : The packing parameters of type vm_packing_params_t
20 type_str - str : The type to cast the unpacked pointer into
24 if params
.vmpp_base_relative
:
25 addr
= unsigned(packed
) << int(params
.vmpp_shift
)
26 if addr
: addr
+= int(params
.vmpp_base
)
28 bits
= int(params
.vmpp_bits
)
29 shift
= int(params
.vmpp_shift
)
30 addr
= c_int64(unsigned(packed
) << (64 - bits
)).value
31 addr
>>= 64 - bits
- shift
32 return kern
.GetValueFromAddress(addr
, type_str
)
34 def GetZPerCPU(root
, cpu
, element_type
= None):
35 """ Iterates over a percpu variable
37 root - value : Value object for per-cpu variable
38 cpu - int : the CPU number
39 element_type - str : Type of element
43 pagesize
= kern
.globals.page_size
44 mangle
= 1 << (8 * kern
.ptrsize
- 1)
45 if element_type
is None:
46 element_type
= root
.GetSBValue().GetType()
47 return kern
.GetValueFromAddress((int(root
) | mangle
) + cpu
* pagesize
, element_type
)
49 def IterateZPerCPU(root
, element_type
= None):
50 """ Iterates over a percpu variable
52 root - value : Value object for per-cpu variable
53 element_type - str : Type of element
57 for i
in range(0, kern
.globals.zpercpu_early_count
):
58 yield GetZPerCPU(root
, i
, element_type
)
60 @lldb_command('showzpcpu', "S")
61 def ShowZPerCPU(cmd_args
=None, cmd_options
={}):
62 """ Routine to show per-cpu zone allocated variables
64 Usage: showzpcpu [-S] expression [field]
65 -S : sum the values instead of printing them
68 raise ArgumentError("No arguments passed")
69 pagesize
= kern
.globals.page_size
70 mangle
= 1 << (8 * kern
.ptrsize
- 1)
71 sbv
= pagesize
.GetSBValue()
72 v
= sbv
.CreateValueFromExpression(None, cmd_args
[0])
75 for i
in range(0, kern
.globals.zpercpu_early_count
):
76 if len(cmd_args
) == 1:
77 t
= sbv
.CreateValueFromExpression(None, '(%s)%d' % (v
.GetTypeName(), (int(e
) | mangle
) + i
* pagesize
)).Dereference()
79 t
= sbv
.CreateValueFromExpression(None, '((%s)%d)->%s' % (v
.GetTypeName(), (int(e
) | mangle
) + i
* pagesize
, cmd_args
[1]))
80 if "-S" in cmd_options
:
85 if "-S" in cmd_options
:
89 """ Formats the name for a given zone
91 zone - value : A pointer to a zone
93 the formated name for the zone
95 names
= [ "", "default.", "data.", "kext."]
96 return "{:s}{:s}".format(names
[int(zone
.kalloc_heap
)], zone
.z_name
)
98 def PrettyPrintDictionary(d
):
99 """ Internal function to pretty print a dictionary with string or integer values
100 params: The dictionary to print
102 for key
, value
in d
.items():
104 if isinstance(value
, int):
105 print "{:<30s} {: >10d}".format(key
, value
)
107 print "{:<30s} {: >10s}".format(key
, value
)
110 @lldb_command('memstats', 'J')
111 def Memstats(cmd_args
=None, cmd_options
={}):
112 """ 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.
117 if "-J" in cmd_options
:
122 memstats
["memorystatus_level"] = int(kern
.globals.memorystatus_level
)
123 memstats
["memorystatus_available_pages"] = int(kern
.globals.memorystatus_available_pages
)
124 memstats
["inuse_ptepages_count"] = int(kern
.globals.inuse_ptepages_count
)
127 if hasattr(kern
.globals, 'compressor_object'):
128 memstats
["compressor_page_count"] = int(kern
.globals.compressor_object
.resident_page_count
)
129 memstats
["vm_page_throttled_count"] = int(kern
.globals.vm_page_throttled_count
)
130 memstats
["vm_page_active_count"] = int(kern
.globals.vm_page_active_count
)
131 memstats
["vm_page_inactive_count"] = int(kern
.globals.vm_page_inactive_count
)
132 memstats
["vm_page_wire_count"] = int(kern
.globals.vm_page_wire_count
)
133 memstats
["vm_page_free_count"] = int(kern
.globals.vm_page_free_count
)
134 memstats
["vm_page_purgeable_count"] = int(kern
.globals.vm_page_purgeable_count
)
135 memstats
["vm_page_inactive_target"] = int(kern
.globals.vm_page_inactive_target
)
136 memstats
["vm_page_free_target"] = int(kern
.globals.vm_page_free_target
)
137 memstats
["vm_page_free_reserved"] = int(kern
.globals.vm_page_free_reserved
)
140 print json
.dumps(memstats
)
142 PrettyPrintDictionary(memstats
)
144 @xnudebug_test('test_memstats')
145 def TestMemstats(kernel_target
, config
, lldb_obj
, isConnected
):
146 """ Test the functionality of memstats command
152 print "Target is not connected. Cannot test memstats"
154 res
= lldb
.SBCommandReturnObject()
155 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("memstats", res
)
156 result
= res
.GetOutput()
157 if result
.split(":")[1].strip().find('None') == -1 :
164 # Macro: showmemorystatus
165 def CalculateLedgerPeak(phys_footprint_entry
):
166 """ Internal function to calculate ledger peak value for the given phys footprint entry
167 params: phys_footprint_entry - value representing struct ledger_entry *
168 return: value - representing the ledger peak for the given phys footprint entry
170 now
= kern
.globals.sched_tick
/ 20
171 ledger_peak
= long(phys_footprint_entry
.le_credit
) - long(phys_footprint_entry
.le_debit
)
172 if hasattr(phys_footprint_entry
._le
._le
_max
, 'le_interval_max') and (long(phys_footprint_entry
._le
._le
_max
.le_interval_max
) > ledger_peak
):
173 ledger_peak
= long(phys_footprint_entry
._le
._le
_max
.le_interval_max
)
176 @header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s} {: <32s}\n".format(
177 'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint',
178 'recent peak', 'lifemax', 'limit', 'command'))
179 def GetMemoryStatusNode(proc_val
):
180 """ Internal function to get memorystatus information from the given proc
181 params: proc - value representing struct proc *
182 return: str - formatted output information for proc object
185 task_val
= Cast(proc_val
.task
, 'task *')
186 task_ledgerp
= task_val
.ledger
188 task_physmem_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.phys_mem
]
189 task_iokit_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.iokit_mapped
]
190 task_phys_footprint_ledger_entry
= task_ledgerp
.l_entries
[kern
.globals.task_ledgers
.phys_footprint
]
191 page_size
= kern
.globals.page_size
193 phys_mem_footprint
= (long(task_physmem_footprint_ledger_entry
.le_credit
) - long(task_physmem_footprint_ledger_entry
.le_debit
)) / page_size
194 iokit_footprint
= (long(task_iokit_footprint_ledger_entry
.le_credit
) - long(task_iokit_footprint_ledger_entry
.le_debit
)) / page_size
195 phys_footprint
= (long(task_phys_footprint_ledger_entry
.le_credit
) - long(task_phys_footprint_ledger_entry
.le_debit
)) / page_size
196 phys_footprint_limit
= long(task_phys_footprint_ledger_entry
.le_limit
) / page_size
197 ledger_peak
= CalculateLedgerPeak(task_phys_footprint_ledger_entry
)
198 phys_footprint_spike
= ledger_peak
/ page_size
199 phys_footprint_lifetime_max
= long(task_phys_footprint_ledger_entry
._le
._le
_max
.le_lifetime_max
) / page_size
201 format_string
= '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}'
202 out_str
+= format_string
.format(proc_val
.p_pid
, proc_val
.p_memstat_effectivepriority
,
203 proc_val
.p_memstat_requestedpriority
, proc_val
.p_memstat_state
, proc_val
.p_memstat_relaunch_flags
,
204 proc_val
.p_memstat_userdata
, phys_mem_footprint
, iokit_footprint
, phys_footprint
)
205 if phys_footprint
!= phys_footprint_spike
:
206 out_str
+= "{: >12d}".format(phys_footprint_spike
)
208 out_str
+= "{: >12s}".format('-')
210 out_str
+= "{: >10d} ".format(phys_footprint_lifetime_max
)
211 out_str
+= "{: >10d} {: <32s}\n".format(phys_footprint_limit
, GetProcName(proc_val
))
214 @lldb_command('showmemorystatus')
215 def ShowMemoryStatus(cmd_args
=None):
216 """ Routine to display each entry in jetsam list with a summary of pressure statistics
217 Usage: showmemorystatus
221 print GetMemoryStatusNode
.header
222 print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)",
223 "(pages)", "(pages)", "(pages)")
224 while bucket_index
< bucket_count
:
225 current_bucket
= kern
.globals.memstat_bucket
[bucket_index
]
226 current_list
= current_bucket
.list
227 current_proc
= Cast(current_list
.tqh_first
, 'proc *')
228 while unsigned(current_proc
) != 0:
229 print GetMemoryStatusNode(current_proc
)
230 current_proc
= current_proc
.p_memstat_list
.tqe_next
235 # EndMacro: showmemorystatus
237 class ZoneMeta(object):
239 Helper class that helpers walking metadata
242 def __init__(self
, addr
, isPageIndex
= False):
244 pagesize
= kern
.globals.page_size
245 zone_info
= kern
.GetGlobalVariable('zone_info')
248 return (unsigned(var
.min_address
), unsigned(var
.max_address
))
251 return x
>= r
[0] and x
<= r
[1]
253 FOREIGN
= GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_FOREIGN')
254 NATIVE
= GetEnumValue('zone_addr_kind_t', 'ZONE_ADDR_NATIVE')
256 self
.meta_range
= load_range(zone_info
.zi_meta_range
)
257 self
.native_range
= load_range(zone_info
.zi_map_range
[NATIVE
])
258 self
.foreign_range
= load_range(zone_info
.zi_map_range
[FOREIGN
])
259 self
.addr_base
= min(self
.foreign_range
[0], self
.native_range
[0])
261 addr
= unsigned(addr
)
264 addr
= value(pagesize
.GetSBValue().CreateValueFromExpression(None,
265 '(long)(int)%d * %d' %(addr
, pagesize
)))
266 addr
= unsigned(addr
)
270 if in_range(addr
, self
.meta_range
):
271 self
.kind
= 'Metadata'
272 addr
-= addr
% sizeof('struct zone_page_metadata')
273 self
.meta_addr
= addr
274 self
.meta
= kern
.GetValueFromAddress(addr
, "struct zone_page_metadata *")
276 self
.page_addr
= self
.addr_base
+ ((addr
- self
.meta_range
[0]) / sizeof('struct zone_page_metadata') * pagesize
)
277 elif in_range(addr
, self
.native_range
) or in_range(addr
, self
.foreign_range
):
278 addr
&= ~
(pagesize
- 1)
279 page_idx
= (addr
- self
.addr_base
) / pagesize
281 self
.kind
= 'Element'
282 self
.page_addr
= addr
283 self
.meta_addr
= self
.meta_range
[0] + page_idx
* sizeof('struct zone_page_metadata')
284 self
.meta
= kern
.GetValueFromAddress(self
.meta_addr
, "struct zone_page_metadata *")
286 self
.kind
= 'Unknown'
292 self
.zone
= addressof(kern
.globals.zone_array
[self
.meta
.zm_index
])
296 def isSecondaryPage(self
):
297 return self
.meta
and self
.meta
.zm_chunk_len
>= 0xe
299 def getPageCount(self
):
300 n
= self
.meta
and self
.meta
.zm_chunk_len
or 0
301 if self
.zone
and self
.zone
.z_percpu
:
302 n
*= kern
.globals.zpercpu_early_count
305 def getAllocAvail(self
):
306 if not self
.meta
: return 0
307 chunk_len
= unsigned(self
.meta
.zm_chunk_len
)
308 page_size
= unsigned(kern
.globals.page_size
)
309 return chunk_len
* page_size
/ self
.zone
.z_elem_size
311 def getAllocCount(self
):
312 if not self
.meta
: return 0
313 return self
.meta
.zm_alloc_size
/ self
.zone
.z_elem_size
316 if self
.isSecondaryPage():
317 return ZoneMeta(unsigned(self
.meta
) - sizeof('struct zone_page_metadata') * unsigned(self
.meta
.zm_page_index
))
321 def getElementAddress(self
, addr
):
322 meta
= self
.getReal()
323 esize
= meta
.zone
.z_elem_size
324 start
= meta
.page_addr
329 estart
= addr
- start
330 return unsigned(start
+ estart
- (estart
% esize
))
332 def getInlineBitmapChunkLength(self
):
333 if self
.zone
.z_percpu
:
334 return unsigned(self
.zone
.z_chunk_pages
)
335 return unsigned(self
.meta
.zm_chunk_len
)
337 def getBitmapSize(self
):
338 if not self
.meta
or self
.zone
.z_permanent
or not self
.meta
.zm_chunk_len
:
340 if self
.meta
.zm_inline_bitmap
:
341 return -4 * self
.getInlineBitmapChunkLength()
342 return 8 << (unsigned(self
.meta
.zm_bitmap
) & 0x7);
345 if not self
.meta
or self
.zone
.z_permanent
or not self
.meta
.zm_chunk_len
:
347 if self
.meta
.zm_inline_bitmap
:
348 return unsigned(addressof(self
.meta
.zm_bitmap
))
349 bbase
= unsigned(kern
.globals.zone_info
.zi_bits_range
.min_address
)
350 index
= unsigned(self
.meta
.zm_bitmap
) & ~
0x7
351 return bbase
+ index
;
353 def getFreeCountSlow(self
):
354 if not self
.meta
or self
.zone
.z_permanent
or not self
.meta
.zm_chunk_len
:
355 return self
.getAllocAvail() - self
.getAllocCount()
358 if self
.meta
.zm_inline_bitmap
:
359 for i
in xrange(0, self
.getInlineBitmapChunkLength()):
360 m
= kern
.GetValueFromAddress(self
.meta_addr
+ i
* 16,
361 'struct zone_page_metadata *');
362 bits
= unsigned(m
.zm_bitmap
)
367 bitmap
= kern
.GetValueFromAddress(self
.getBitmap(), 'uint64_t *')
368 for i
in xrange(0, 1 << (unsigned(self
.meta
.zm_bitmap
) & 0x7)):
369 bits
= unsigned(bitmap
[i
])
375 def isElementFree(self
, addr
):
378 if not meta
or self
.zone
.z_permanent
or not meta
.zm_chunk_len
:
381 start
= self
.page_addr
382 esize
= self
.zone
.z_elem_size
383 eidx
= (addr
- start
) / esize
385 if meta
.zm_inline_bitmap
:
387 m
= unsigned(meta
) + sizeof('struct zone_page_metadata') * i
388 bits
= kern
.GetValueFromAddress(m
, meta
).zm_bitmap
389 return (bits
& (1 << (eidx
% 32))) != 0
392 bitmap
= kern
.GetValueFromAddress(self
.getBitmap(), 'uint64_t *')
393 bits
= unsigned(bitmap
[eidx
/ 64])
394 return (bits
& (1 << (eidx
% 64))) != 0
396 def iterateElements(self
):
397 if self
.meta
is None:
399 esize
= self
.zone
.z_elem_size
401 end
= unsigned(kern
.globals.page_size
) * self
.meta
.zm_chunk_len
404 for offs
in xrange(start
, end
, esize
):
405 yield unsigned(self
.page_addr
+ offs
)
407 @lldb_type_summary(['zone_page_metadata'])
408 @header("{:<20s} {:<10s} {:<10s} {:<24s} {:<20s} {:<20s}".format(
409 'METADATA', 'PG_CNT', 'ALLOC_CNT', 'BITMAP', 'ZONE', 'NAME'))
410 def GetZoneMetadataSummary(meta
):
411 """ Summarize a zone metadata object
412 params: meta - obj representing zone metadata in the kernel
413 returns: str - summary of the zone metadata
416 if type(meta
) != ZoneMeta
:
417 meta
= ZoneMeta(meta
)
419 out_str
= 'Metadata Description:\n' + GetZoneMetadataSummary
.header
+ '\n'
420 if meta
.isSecondaryPage():
421 out_str
+= "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}\n".format(
422 meta
.meta_addr
, 0, 0, 0, 0, 0, '(fake multipage meta)')
423 meta
= meta
.getReal()
424 out_str
+= "{:<#20x} {:<10d} {:<10d} {:<#18x} @{:<4d} {:<#20x} {:s}".format(
425 meta
.meta_addr
, meta
.getPageCount(), meta
.getAllocCount(),
426 meta
.getBitmap(), meta
.getBitmapSize(), meta
.zone
, ZoneName(meta
.zone
))
429 @header("{:<20s} {:<10s} {:<10s} {:<20s} {:<10s}".format(
430 'ADDRESS', 'TYPE', 'STATUS', 'PAGE_ADDR', 'OFFSET'))
432 """ Information about kernel pointer
436 meta
= ZoneMeta(addr
)
439 if meta
.meta
is None:
440 out_str
= "Address {:#018x} is outside of any zone map ({:#018x}-{:#018x})\n".format(
441 addr
, meta
.native_range
[0], meta
.native_range
[-1] + 1)
443 if meta
.kind
[0] == 'E': # element
444 page_offset_str
= "{:d}/{:d}K".format(
445 addr
- meta
.page_addr
, kern
.globals.page_size
/ 1024)
446 estart
= meta
.getElementAddress(addr
)
448 status
= "Unattributed"
449 elif meta
.isElementFree(estart
):
454 page_offset_str
= "-"
456 out_str
= WhatIs
.header
+ '\n'
457 out_str
+= "{meta.address:<#20x} {meta.kind:<10s} {status:<10s} {meta.page_addr:<#20x} {:<10s}\n\n".format(
458 page_offset_str
, meta
=meta
, status
=status
)
459 out_str
+= GetZoneMetadataSummary(meta
) + '\n\n'
463 if estart
is not None:
466 meta
= meta
.getReal()
467 esize
= meta
.zone
.z_elem_size
468 start
= meta
.page_addr
469 marks
= {unsigned(addr): ">"}
473 data_array
= kern
.GetValueFromAddress(estart
- 16, "uint8_t *")
474 print_hex_data(data_array
[0:16], estart
- 16, "")
478 print "------------------------------------------------------------------"
480 data_array
= kern
.GetValueFromAddress(estart
, "uint8_t *")
481 print_hex_data(data_array
[0:esize
], estart
, "", marks
)
483 print "*** unable to read memory ***"
485 print "------------------------------------------------------------------"
488 data_array
= kern
.GetValueFromAddress(estart
+ esize
, "uint8_t *")
489 print_hex_data(data_array
[0:16], estart
+ esize
, "")
493 @lldb_command('whatis')
494 def WhatIsHelper(cmd_args
=None):
495 """ Routine to show information about a kernel pointer
496 Usage: whatis <address>
499 raise ArgumentError("No arguments passed")
500 WhatIs(kern
.GetValueFromAddress(cmd_args
[0], 'void *'))
504 @lldb_type_summary(['zone','zone_t'])
505 @header("{:18s} {:32s} {:>6s} {:>6s} {:>6s} {:>6s} {:>6s} {:>6s} {:<s}".format(
506 'ZONE', 'NAME', 'WSS', 'CONT', 'USED', 'FREE', 'CACHED', 'RECIRC', 'CPU_CACHES'))
507 def GetZoneCacheCPUSummary(zone
, verbose
, O
):
508 """ Summarize a zone's cache broken up per cpu
510 zone: value - obj representing a zone in kernel
512 str - summary of the zone's per CPU cache contents
514 format_string
= '{zone:#018x} {:32s} '
515 format_string
+= '{zone.z_elems_free_wss:6d} {cont:6.2f} '
516 format_string
+= '{used:6d} {zone.z_elems_free:6d} '
517 format_string
+= '{cached:6d} {recirc:6d} {cpuinfo:s}'
520 mag_capacity
= unsigned(kern
.GetGlobalVariable('zc_magazine_size'))
521 depot_capacity
= kern
.GetGlobalVariable('depot_element_count')
523 if zone
.z_pcpu_cache
:
526 for cache
in IterateZPerCPU(zone
.z_pcpu_cache
):
531 per_cpu_count
= unsigned(cache
.zc_alloc_cur
)
532 per_cpu_count
+= unsigned(cache
.zc_free_cur
)
533 per_cpu_count
+= unsigned(cache
.zc_depot_cur
) * mag_capacity
534 cache_elem_count
+= per_cpu_count
535 cpu_info
+= "{:3d} /{cache.zc_depot_max:3d}".format(per_cpu_count
, cache
=cache
)
540 for cache
in IterateZPerCPU(zone
.z_pcpu_cache
):
541 depot_cur
+= unsigned(cache
.zc_alloc_cur
)
542 depot_cur
+= unsigned(cache
.zc_free_cur
)
543 cache_elem_count
+= unsigned(cache
.zc_depot_cur
) * mag_capacity
544 depot_max
+= unsigned(cache
.zc_depot_max
)
545 cache_elem_count
+= depot_cur
547 cpus
= unsigned(kern
.globals.zpercpu_early_count
)
548 cpu_info
= "total: {:3d} / {:3d}, avg: {:5.1f} / {:5.1f}".format(
549 depot_cur
, depot_max
, float(depot_cur
) / cpus
, float(depot_max
) / cpus
)
552 print O
.format(format_string
, ZoneName(zone
), cached
=cache_elem_count
,
553 used
=zone
.z_elems_avail
- cache_elem_count
- zone
.z_elems_free
,
554 cont
=float(zone
.z_contention_wma
) / 256.,
555 recirc
=zone
.z_recirc_cur
* mag_capacity
,
556 zone
=zone
, cpuinfo
= cpu_info
)
558 @lldb_command('showzcache', fancy
=True)
559 def ZcacheCPUPrint(cmd_args
=None, cmd_options
={}, O
=None):
561 Routine to print a summary listing of all the kernel zones cache contents
563 Usage: showzcache [-V]
565 Use -V to see more detailed output
568 verbose
= "-V" in cmd_options
569 with O
.table(GetZoneCacheCPUSummary
.header
):
570 if len(cmd_args
) == 1:
571 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
572 GetZoneCacheCPUSummary(zone
, verbose
, O
);
574 for zval
in kern
.zones
:
576 GetZoneCacheCPUSummary(zval
, verbose
, O
)
578 # EndMacro: showzcache
582 def GetZone(zone_val
, marks
):
583 """ Internal function which gets a phython dictionary containing important zone information.
585 zone_val: value - obj representing a zone in kernel
587 zone - python dictionary with zone stats
590 if zone_val
.z_percpu
:
591 pcpu_scale
= unsigned(kern
.globals.zpercpu_early_count
)
592 pagesize
= kern
.globals.page_size
594 mag_capacity
= unsigned(kern
.GetGlobalVariable('zc_magazine_size'))
595 zone
["page_count"] = unsigned(zone_val
.z_wired_cur
) * pcpu_scale
596 zone
["allfree_page_count"] = unsigned(zone_val
.z_wired_empty
)
599 if zone_val
.z_pcpu_cache
:
600 for cache
in IterateZPerCPU(zone_val
.z_pcpu_cache
):
601 cache_elem_count
+= unsigned(cache
.zc_alloc_cur
)
602 cache_elem_count
+= unsigned(cache
.zc_free_cur
)
603 cache_elem_count
+= unsigned(cache
.zc_depot_cur
) * mag_capacity
605 zone
["size"] = zone
["page_count"] * pagesize
607 zone
["free_size"] = zone_val
.z_elems_free
* zone_val
.z_elem_size
* pcpu_scale
608 zone
["cached_size"] = cache_elem_count
* zone_val
.z_elem_size
* pcpu_scale
609 zone
["used_size"] = zone
["size"] - zone
["free_size"] - zone
["cached_size"]
611 zone
["element_count"] = zone_val
.z_elems_avail
- zone_val
.z_elems_free
- cache_elem_count
612 zone
["cache_element_count"] = cache_elem_count
613 zone
["free_element_count"] = zone_val
.z_elems_free
615 if zone_val
.z_percpu
:
616 zone
["allocation_size"] = unsigned(pagesize
)
617 zone
["allocation_ncpu"] = unsigned(zone_val
.z_chunk_pages
)
619 zone
["allocation_size"] = unsigned(zone_val
.z_chunk_pages
* pagesize
)
620 zone
["allocation_ncpu"] = 1
621 zone
["allocation_count"] = zone
["allocation_size"] / zone_val
.z_elem_size
622 zone
["allocation_waste"] = (zone
["allocation_size"] % zone_val
.z_elem_size
) * zone
["allocation_ncpu"]
624 if not zone_val
.__getattr
__("z_self") :
625 zone
["destroyed"] = True
627 zone
["destroyed"] = False
630 if zone_val
.__getattr
__(mark
[0]):
633 zone
[mark
[0]] = False
635 zone
["name"] = ZoneName(zone_val
)
636 if zone_val
.exhaustible
:
637 zone
["exhaustible"] = True
639 zone
["exhaustible"] = False
641 zone
["sequester_page_count"] = (unsigned(zone_val
.z_va_cur
) -
642 unsigned(zone_val
.z_wired_cur
)) * pcpu_scale
643 zone
["page_count_max"] = unsigned(zone_val
.z_wired_max
) * pcpu_scale
648 @lldb_type_summary(['zone','zone_t'])
649 @header(("{:<18s} {:_^47s} {:_^24s} {:_^13s} {:_^28s}\n"+
650 "{:<18s} {:>11s} {:>11s} {:>11s} {:>11s} {:>8s} {:>7s} {:>7s} {:>6s} {:>6s} {:>8s} {:>6s} {:>5s} {:>7s} {:<18s} {:<20s}").format(
651 '', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG',
652 'ZONE', 'TOTAL', 'ALLOC', 'CACHE', 'FREE', 'ALLOC', 'CACHE', 'FREE', 'COUNT', 'FREE', 'SIZE (P)', 'ELTS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
653 def GetZoneSummary(zone_val
, marks
, stats
):
654 """ Summarize a zone with important information. See help zprint for description of each field
656 zone_val: value - obj representing a zone in kernel
658 str - summary of the zone
660 pagesize
= kern
.globals.page_size
662 zone
= GetZone(zone_val
, marks
)
665 if zone_val
.z_percpu
:
666 pcpu_scale
= unsigned(kern
.globals.zpercpu_early_count
)
668 format_string
= '{zone:#018x} {zd[size]:11,d} {zd[used_size]:11,d} {zd[cached_size]:11,d} {zd[free_size]:11,d} '
669 format_string
+= '{zd[element_count]:8,d} {zd[cache_element_count]:7,d} {zone.z_elems_free:7,d} '
670 format_string
+= '{z_wired_cur:6,d} {z_wired_empty:6,d} '
671 format_string
+= '{alloc_size_kb:3,d}K ({zone.z_chunk_pages:d}) '
672 format_string
+= '{zd[allocation_count]:6,d} {zd[allocation_waste]:5,d} {z_elem_size:7,d} '
673 format_string
+= '{markings:<18s} {zone_name:<20s}'
676 if zone
["destroyed"]:
685 alloc_size_kb
= zone
["allocation_size"] / 1024
686 out_string
+= format_string
.format(zone
=zone_val
, zd
=zone
,
687 z_wired_cur
=unsigned(zone_val
.z_wired_cur
) * pcpu_scale
,
688 z_wired_empty
=unsigned(zone_val
.z_wired_empty
) * pcpu_scale
,
689 z_elem_size
=unsigned(zone_val
.z_elem_size
) * pcpu_scale
,
690 alloc_size_kb
=alloc_size_kb
, markings
=markings
, zone_name
=zone
["name"])
692 if zone
["exhaustible"] :
693 out_string
+= " (max: {:d})".format(zone
["page_count_max"] * pagesize
)
695 if zone
["sequester_page_count"] != 0 :
696 out_string
+= " (sequester: {:d})".format(zone
["sequester_page_count"])
698 stats
["cur_size"] += zone
["size"]
699 stats
["used_size"] += zone
["used_size"]
700 stats
["cached_size"] += zone
["cached_size"]
701 stats
["free_size"] += zone
["free_size"]
702 stats
["cur_pages"] += zone
["page_count"]
703 stats
["free_pages"] += zone
["allfree_page_count"]
704 stats
["seq_pages"] += zone
["sequester_page_count"]
708 @lldb_command('zprint', "J", fancy
=True)
709 def Zprint(cmd_args
=None, cmd_options
={}, O
=None):
710 """ Routine to print a summary listing of all the kernel zones
713 All columns are printed in decimal
715 ! - zone uses VA sequestering
716 $ - not encrypted during hibernation
717 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
720 E - Per-cpu caching is enabled for this zone
721 F - allows foreign memory (memory not allocated from any zone map)
722 G - currently running GC
724 I - zone was destroyed and is no longer valid
725 L - zone is being monitored by zleaks
726 M - gzalloc will avoid monitoring this zone
727 N - zone requires alignment (avoids padding this zone for debugging)
728 O - does not allow refill callout to fill zone on noblock allocation
729 R - will be refilled when below low water mark
730 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
731 W - another thread is waiting for more memory
733 Z - elements are zeroed on free
738 ["collectable", "C"],
739 ["z_destructible", "D"],
741 ["z_noencrypt", "$"],
742 ["exhaustible", "H"],
743 ["z_allows_foreign", "F"],
744 ["z_elems_rsv", "R"],
748 ["z_expander_vm_priv", "S"],
749 ["z_replenish_wait", "W"],
750 ["z_pcpu_cache", "E"],
751 ["gzalloc_exempt", "M"],
752 ["alignment_required", "N"],
753 ["z_va_sequester", "!"],
754 ["z_free_zeroes", "Z"]
758 "cur_size": 0, "used_size": 0, "cached_size": 0, "free_size": 0,
759 "cur_pages": 0, "free_pages": 0, "seq_pages": 0
763 if "-J" in cmd_options
:
768 for zval
in kern
.zones
:
770 zones
.append(GetZone(zval
, marks
))
772 print json
.dumps(zones
)
774 with O
.table(GetZoneSummary
.header
):
775 for zval
in kern
.zones
:
777 print GetZoneSummary(zval
, marks
, stats
)
779 format_string
= '{VT.Bold}{name:19s} {stats[cur_size]:11,d} {stats[used_size]:11,d} {stats[cached_size]:11,d} {stats[free_size]:11,d} '
781 format_string
+= '{stats[cur_pages]:6,d} {stats[free_pages]:6,d}{VT.EndBold} '
782 format_string
+= '(sequester: {VT.Bold}{stats[seq_pages]:,d}{VT.EndBold})'
783 print O
.format(format_string
, name
="TOTALS", filler
="", stats
=stats
)
786 @xnudebug_test('test_zprint')
787 def TestZprint(kernel_target
, config
, lldb_obj
, isConnected
):
788 """ Test the functionality of zprint command
794 print "Target is not connected. Cannot test memstats"
796 res
= lldb
.SBCommandReturnObject()
797 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("zprint", res
)
798 result
= res
.GetOutput()
799 if len(result
.split("\n")) > 2:
808 def ZoneIteratePageQueue(page
):
809 while page
.packed_address
:
810 meta
= ZoneMeta(page
.packed_address
, isPageIndex
=True)
812 page
= meta
.meta
.zm_page_next
814 @header("{: <20s} {: <20s} {: <20s} {: <25s} {: <10s} {: <8s} {: <4s} {: >9s}".format(
815 "Zone", "Metadata", "Page", "Bitmap", "Kind", "Queue", "Pgs", "Allocs"))
816 def GetZoneChunk(meta
, queue
, O
=None):
817 format_string
= "{meta.zone: <#20x} "
818 format_string
+= "{meta.meta_addr: <#20x} {meta.page_addr: <#20x} "
819 format_string
+= "{bitmap: <#18x} @{bitmap_size:<5d} "
820 format_string
+= "{kind:<10s} {queue:<8s} {pgs:<1d}/{chunk:<1d} "
821 format_string
+= "{alloc_count: >4d}/{avail_count: >4d}"
823 pgs
= int(meta
.zone
.z_chunk_pages
)
825 if meta
.meta
.zm_chunk_len
>= 0xe:
827 pgs
-= int(meta
.meta
.zm_page_index
)
831 alloc_count
=meta
.getAllocCount()
832 avail_count
=meta
.getAllocAvail()
833 free_count
=meta
.getFreeCountSlow()
835 if alloc_count
+ free_count
!= avail_count
:
836 format_string
+= " {VT.Red}bitmap mismatch{VT.Default}"
838 return O
.format(format_string
, meta
=meta
,
839 alloc_count
=alloc_count
,
840 avail_count
=avail_count
,
841 bitmap
=meta
.getBitmap(),
842 bitmap_size
=meta
.getBitmapSize(),
843 queue
=queue
, kind
=kind
, pgs
=pgs
, chunk
=chunk
)
845 def ShowZChunksImpl(zone
, extra_addr
=None, cmd_options
={}, O
=None):
846 verbose
= '-V' in cmd_options
848 def do_content(meta
, O
, indent
=False):
849 with O
.table("{:>5s} {:<20s} {:<10s}".format("#", "Element", "State"), indent
=indent
):
851 for e
in meta
.iterateElements():
853 if meta
.isElementFree(e
):
855 print O
.format("{:5d} {:<#20x} {:10s}", i
, e
, status
)
858 if extra_addr
is None:
859 with O
.table(GetZoneChunk
.header
):
860 for meta
in ZoneIteratePageQueue(zone
.z_pageq_full
):
861 print GetZoneChunk(meta
, "full", O
)
862 if verbose
: do_content(meta
, O
, indent
=True);
864 for meta
in ZoneIteratePageQueue(zone
.z_pageq_partial
):
865 print GetZoneChunk(meta
, "partial", O
)
866 if verbose
: do_content(meta
, O
, indent
=True);
868 for meta
in ZoneIteratePageQueue(zone
.z_pageq_empty
):
869 print GetZoneChunk(meta
, "empty", O
)
870 if verbose
: do_content(meta
, O
, indent
=True);
872 for meta
in ZoneIteratePageQueue(zone
.z_pageq_va
):
873 print GetZoneChunk(meta
, "va", O
)
875 meta
= ZoneMeta(extra_addr
, isPageIndex
="-I" in cmd_options
).getReal()
876 with O
.table(GetZoneChunk
.header
):
877 print GetZoneChunk(meta
, "N/A", O
)
880 @lldb_command('showzchunks', "IV", fancy
=True)
881 def ShowZChunks(cmd_args
=None, cmd_options
={}, O
=None):
883 prints the list of zone chunks, or the content of a given chunk
885 Usage: showzchunks <zone> [-I] [-V] [address]
887 Use -I to interpret [address] as a page index
888 Use -V to show the contents of all the chunks
890 [address] can by any address belonging to the zone, or metadata
894 return O
.error('missing zone argument')
896 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
898 if len(cmd_args
) == 1:
899 ShowZChunksImpl(zone
, cmd_options
=cmd_options
, O
=O
)
901 addr
= unsigned(kern
.GetValueFromAddress(cmd_args
[1]))
902 ShowZChunksImpl(zone
, extra_addr
=addr
, cmd_options
=cmd_options
, O
=O
)
904 @lldb_command('showallzchunks', fancy
=True)
905 def ShowAllZChunks(cmd_args
=None, cmd_options
={}, O
=None):
907 prints the list of all zone chunks
909 Usage: showallzchunks
913 ShowZChunksImpl(z
, O
=O
)
915 # EndMacro: showzchunks
916 # Macro: zstack_showzonesbeinglogged
918 @lldb_command('zstack_showzonesbeinglogged')
919 def ZstackShowZonesBeingLogged(cmd_args
=None):
920 """ Show all zones which have BTLog enabled.
923 for zval
in kern
.zones
:
925 print "Zone: %s with its BTLog at: 0x%lx" % (ZoneName(zval
), zval
.zlog_btlog
)
927 # EndMacro: zstack_showzonesbeinglogged
931 @lldb_command('zstack')
932 def Zstack(cmd_args
=None):
933 """ 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>.
934 Usage: zstack <btlog addr> <index> [<count>]
936 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
937 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
942 if int(kern
.globals.log_records
) == 0:
943 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
946 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
947 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
948 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
949 btrecords
= unsigned(btlog_ptr
.btrecords
)
950 btlog_size
= unsigned(sizeof('struct btlog'))
951 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
952 zstack_index
= ArgumentStringToInt(cmd_args
[1])
954 if len(cmd_args
) >= 3:
955 count
= ArgumentStringToInt(cmd_args
[2])
957 max_count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
959 if (zstack_index
+ count
) > max_count
:
960 count
= max_count
- zstack_index
962 while count
and (zstack_index
!= 0xffffff):
963 zstack_record_offset
= zstack_index
* btrecord_size
964 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
965 if int(zstack_record
.ref_count
)!=0:
966 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
972 # Macro: zstack_inorder
974 @lldb_command('zstack_inorder')
975 def ZstackInOrder(cmd_args
=None):
976 """ Zone leak debugging: Print the stack traces starting from head to the tail.
977 Usage: zstack_inorder <btlog addr>
980 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
982 if int(kern
.globals.log_records
) == 0:
983 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
986 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
987 btrecords_total_size
= unsigned(btlog_ptr
.btlog_buffersize
)
988 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
989 btrecords
= unsigned(btlog_ptr
.btrecords
)
990 btlog_size
= unsigned(sizeof('struct btlog'))
991 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
992 zstack_head
= unsigned(btlog_ptr
.head
)
993 zstack_index
= zstack_head
994 zstack_tail
= unsigned(btlog_ptr
.tail
)
995 count
= ((btrecords_total_size
- btlog_size
)/btrecord_size
)
997 while count
and (zstack_index
!= 0xffffff):
998 zstack_record_offset
= zstack_index
* btrecord_size
999 zstack_record
= kern
.GetValueFromAddress(btrecords
+ zstack_record_offset
, 'btlog_record_t *')
1000 ShowZStackRecord(zstack_record
, zstack_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
1001 zstack_index
= zstack_record
.next
1004 # EndMacro : zstack_inorder
1008 @lldb_command('findoldest')
1009 def FindOldest(cmd_args
=None):
1012 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
1014 # EndMacro : findoldest
1016 # Macro : zstack_findleak
1018 @lldb_command('zstack_findleak')
1019 def zstack_findleak(cmd_args
=None):
1020 """ Zone leak debugging: search the log and print the stack with the most active references
1022 Usage: zstack_findleak <btlog address>
1024 This is useful for verifying a suspected stack as being the source of
1027 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
1028 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
1029 btrecords
= unsigned(btlog_ptr
.btrecords
)
1031 cpcs_index
= unsigned(btlog_ptr
.head
)
1032 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
1037 while cpcs_index
!= 0xffffff:
1038 cpcs_record_offset
= cpcs_index
* btrecord_size
1039 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
1040 if cpcs_record
.ref_count
> highref
:
1041 highref_record
= cpcs_record
1042 highref
= cpcs_record
.ref_count
1043 highref_index
= cpcs_index
1044 cpcs_index
= cpcs_record
.next
1045 ShowZStackRecord(highref_record
, highref_index
, depth
, unsigned(btlog_ptr
.active_element_count
))
1047 # EndMacro: zstack_findleak
1051 @lldb_command('findelem')
1052 def FindElem(cmd_args
=None):
1055 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
1057 # EndMacro: findelem
1059 @lldb_command('zstack_findelem')
1060 def ZStackFindElem(cmd_args
=None):
1061 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
1062 refer to the given zone element.
1063 Usage: zstack_findelem <btlog addr> <elem addr>
1065 When the kernel panics due to a corrupted zone element, get the
1066 element address and use this command. This will show you the stack traces of all logged zalloc and
1067 zfree operations which tells you who touched the element in the recent past. This also makes
1068 double-frees readily apparent.
1071 print ZStackFindElem
.__doc
__
1073 if int(kern
.globals.log_records
) == 0 or unsigned(kern
.globals.corruption_debug_flag
) == 0:
1074 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
1077 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
1078 target_element
= unsigned(kern
.GetValueFromAddress(cmd_args
[1], 'void *'))
1080 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
1081 btrecords
= unsigned(btlog_ptr
.btrecords
)
1082 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
1086 hashelem
= cast(btlog_ptr
.elem_linkage_un
.element_hash_queue
.tqh_first
, 'btlog_element_t *')
1087 if (target_element
>> 32) != 0:
1088 target_element
= target_element ^
0xFFFFFFFFFFFFFFFF
1090 target_element
= target_element ^
0xFFFFFFFF
1091 while hashelem
!= 0:
1092 if unsigned(hashelem
.elem
) == target_element
:
1093 recindex
= hashelem
.recindex
1094 recoffset
= recindex
* btrecord_size
1095 record
= kern
.GetValueFromAddress(btrecords
+ recoffset
, 'btlog_record_t *')
1097 if record
.operation
== 1:
1098 out_str
+= "OP: ALLOC. "
1100 out_str
+= "OP: FREE. "
1101 out_str
+= "Stack Index {0: <d} {1: <s}\n".format(recindex
, ('-' * 8))
1103 print GetBtlogBacktrace(depth
, record
)
1105 if int(record
.operation
) == prev_op
:
1106 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
1108 prev_op
= int(record
.operation
)
1110 hashelem
= cast(hashelem
.element_hash_link
.tqe_next
, 'btlog_element_t *')
1112 if scan_items
% 100 == 0:
1113 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items
)
1115 # EndMacro: zstack_findelem
1117 @lldb_command('zstack_findtop', 'N:')
1118 def ShowZstackTop(cmd_args
=None, cmd_options
={}):
1119 """ Zone leak debugging: search the log and print the stacks with the most active references
1122 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
1126 raise ArgumentError('Missing required btlog address argument')
1129 if '-N' in cmd_options
:
1130 n
= int(cmd_options
['-N'])
1132 btlog_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'btlog_t *')
1133 btrecord_size
= unsigned(btlog_ptr
.btrecord_size
)
1134 btrecords
= unsigned(btlog_ptr
.btrecords
)
1136 cpcs_index
= unsigned(btlog_ptr
.head
)
1137 depth
= unsigned(btlog_ptr
.btrecord_btdepth
)
1140 while cpcs_index
!= 0xffffff:
1141 cpcs_record_offset
= cpcs_index
* btrecord_size
1142 cpcs_record
= kern
.GetValueFromAddress(btrecords
+ cpcs_record_offset
, 'btlog_record_t *')
1143 cpcs_record
.index
= cpcs_index
1144 records
.append(cpcs_record
)
1145 cpcs_index
= cpcs_record
.next
1147 recs
= sorted(records
, key
=lambda x
: x
.ref_count
, reverse
=True)
1149 for rec
in recs
[:n
]:
1150 ShowZStackRecord(rec
, rec
.index
, depth
, unsigned(btlog_ptr
.active_element_count
))
1152 # EndMacro: zstack_findtop
1156 @lldb_command('btlog_find', "AS")
1157 def BtlogFind(cmd_args
=None, cmd_options
={}):
1160 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
1163 #EndMacro: btlog_find
1167 @lldb_command('showzalloc')
1168 def ShowZalloc(cmd_args
=None):
1169 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
1170 Usage: showzalloc <index>
1173 print ShowZalloc
.__doc
__
1175 if unsigned(kern
.globals.zallocations
) == 0:
1176 print "zallocations array not initialized!"
1178 zallocation
= kern
.globals.zallocations
[ArgumentStringToInt(cmd_args
[0])]
1180 ShowZTrace([str(int(zallocation
.za_trace_index
))])
1182 #EndMacro: showzalloc
1186 @lldb_command('showztrace')
1187 def ShowZTrace(cmd_args
=None):
1188 """ Prints the backtrace from the ztraces array at index
1189 Usage: showztrace <trace index>
1192 print ShowZTrace
.__doc
__
1194 if unsigned(kern
.globals.ztraces
) == 0:
1195 print "ztraces array not initialized!"
1197 ztrace_addr
= kern
.globals.ztraces
[ArgumentStringToInt(cmd_args
[0])]
1199 ShowZstackTraceHelper(ztrace_addr
.zt_stack
, ztrace_addr
.zt_depth
)
1201 #EndMacro: showztrace
1203 #Macro: showztraceaddr
1205 @lldb_command('showztraceaddr')
1206 def ShowZTraceAddr(cmd_args
=None):
1207 """ Prints the struct ztrace passed in.
1208 Usage: showztraceaddr <trace address>
1211 print ShowZTraceAddr
.__doc
__
1213 ztrace_ptr
= kern
.GetValueFromAddress(cmd_args
[0], 'struct ztrace *')
1214 print dereference(ztrace_ptr
)
1215 ShowZstackTraceHelper(ztrace_ptr
.zt_stack
, ztrace_ptr
.zt_depth
)
1217 #EndMacro: showztraceaddr
1219 #Macro: showzstacktrace
1221 @lldb_command('showzstacktrace')
1222 def ShowZstackTrace(cmd_args
=None):
1223 """ Routine to print a stacktrace stored by OSBacktrace.
1224 Usage: showzstacktrace <saved stacktrace> [size]
1226 size is optional, defaults to 15.
1229 print ShowZstackTrace
.__doc
__
1231 void_ptr_type
= gettype('void *')
1232 void_double_ptr_type
= void_ptr_type
.GetPointerType()
1233 trace
= kern
.GetValueFromAddress(cmd_args
[0], void_double_ptr_type
)
1235 if len(cmd_args
) >= 2:
1236 trace_size
= ArgumentStringToInt(cmd_args
[1])
1237 ShowZstackTraceHelper(trace
, trace_size
)
1239 #EndMacro: showzstacktrace
1241 def ShowZstackTraceHelper(stack
, depth
):
1242 """ Helper routine for printing a zstack.
1244 stack: void *[] - An array of pointers representing the Zstack
1245 depth: int - The depth of the ztrace stack
1250 while trace_current
< depth
:
1251 trace_addr
= stack
[trace_current
]
1252 symbol_arr
= kern
.SymbolicateFromAddress(unsigned(trace_addr
))
1254 symbol_str
= str(symbol_arr
[0].addr
)
1257 print '{0: <#x} {1: <s}'.format(trace_addr
, symbol_str
)
1260 #Macro: showtopztrace
1262 @lldb_command('showtopztrace')
1263 def ShowTopZtrace(cmd_args
=None):
1264 """ Shows the ztrace with the biggest size.
1265 (According to top_ztrace, not by iterating through the hash table)
1267 top_trace
= kern
.globals.top_ztrace
1268 print 'Index: {0: <d}'.format((unsigned(top_trace
) - unsigned(kern
.globals.ztraces
)) / sizeof('struct ztrace'))
1269 print dereference(top_trace
)
1270 ShowZstackTraceHelper(top_trace
.zt_stack
, top_trace
.zt_depth
)
1272 #EndMacro: showtopztrace
1276 @lldb_command('showzallocs')
1277 def ShowZallocs(cmd_args
=None):
1278 """ Prints all allocations in the zallocations table
1280 if unsigned(kern
.globals.zallocations
) == 0:
1281 print "zallocations array not initialized!"
1283 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
1285 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
1286 allocation_count
= 0
1287 while current_index
< max_zallocation
:
1288 current_zalloc
= kern
.globals.zallocations
[current_index
]
1289 if int(current_zalloc
.za_element
) != 0:
1290 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
))
1291 allocation_count
+= 1
1293 print 'Total Allocations: {0: <d}'.format(allocation_count
)
1295 #EndMacro: showzallocs
1297 #Macro: showzallocsfortrace
1299 @lldb_command('showzallocsfortrace')
1300 def ShowZallocsForTrace(cmd_args
=None):
1301 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
1302 Usage: showzallocsfortrace <trace index>
1305 print ShowZallocsForTrace
.__doc
__
1307 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
1308 target_index
= ArgumentStringToInt(cmd_args
[0])
1310 max_zallocation
= unsigned(kern
.globals.zleak_alloc_buckets
)
1311 allocation_count
= 0
1312 while current_index
< max_zallocation
:
1313 current_zalloc
= kern
.globals.zallocations
[current_index
]
1314 if unsigned(current_zalloc
.za_element
) != 0 and (unsigned(current_zalloc
.za_trace_index
) == unsigned(target_index
)):
1315 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index
, current_zalloc
.za_element
, current_zalloc
.za_size
)
1316 allocation_count
+= 1
1318 print 'Total Allocations: {0: <d}'.format(allocation_count
)
1320 #EndMacro: showzallocsfortrace
1324 @lldb_command('showztraces')
1325 def ShowZTraces(cmd_args
=None):
1326 """ Prints all traces with size > 0
1328 ShowZTracesAbove([0])
1330 #EndMacro: showztraces
1332 #Macro: showztracesabove
1334 @lldb_command('showztracesabove')
1335 def ShowZTracesAbove(cmd_args
=None):
1336 """ Prints all traces with size greater than X
1337 Usage: showztracesabove <size>
1340 print ShowZTracesAbove
.__doc
__
1342 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
1345 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
1346 while current_index
< max_ztrace
:
1347 ztrace_current
= kern
.globals.ztraces
[current_index
]
1348 if ztrace_current
.zt_size
> unsigned(cmd_args
[0]):
1349 print '{0: <5d} {1: <6d}'.format(current_index
, int(ztrace_current
.zt_size
))
1352 print 'Total traces: {0: <d}'.format(ztrace_count
)
1354 #EndMacro: showztracesabove
1356 #Macro: showztracehistogram
1358 @lldb_command('showztracehistogram')
1359 def ShowZtraceHistogram(cmd_args
=None):
1360 """ Prints the histogram of the ztrace table
1362 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
1365 max_ztrace
= unsigned(kern
.globals.zleak_trace_buckets
)
1366 while current_index
< max_ztrace
:
1367 ztrace_current
= kern
.globals.ztraces
[current_index
]
1368 if ztrace_current
.zt_hit_count
!= 0:
1369 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index
, ztrace_current
.zt_hit_count
, ztrace_current
.zt_collisions
)
1372 print 'Total traces: {0: <d}'.format(ztrace_count
)
1374 #EndMacro: showztracehistogram
1376 #Macro: showzallochistogram
1378 @lldb_command('showzallochistogram')
1379 def ShowZallocHistogram(cmd_args
=None):
1380 """ Prints the histogram for the zalloc table
1382 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
1384 zallocation_count
= 0
1385 max_ztrace
= unsigned(kern
.globals.zleak_alloc_buckets
)
1386 while current_index
< max_ztrace
:
1387 zallocation_current
= kern
.globals.zallocations
[current_index
]
1388 if zallocation_current
.za_hit_count
!= 0:
1389 print '{0: <5d} {1: <9d}'.format(current_index
, zallocation_current
.za_hit_count
)
1390 zallocation_count
+= 1
1392 print 'Total Allocations: {0: <d}'.format(zallocation_count
)
1394 #EndMacro: showzallochistogram
1398 @lldb_command('showzstats')
1399 def ShowZstats(cmd_args
=None):
1400 """ Prints the zone leak detection stats
1402 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern
.globals.z_alloc_collisions
), unsigned(kern
.globals.z_trace_collisions
))
1403 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern
.globals.z_alloc_overwrites
), unsigned(kern
.globals.z_trace_overwrites
))
1404 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern
.globals.z_alloc_recorded
), unsigned(kern
.globals.z_trace_recorded
))
1406 #EndMacro: showzstats
1410 @lldb_command('showpcpu', "N:V", fancy
=True)
1411 def ShowPCPU(cmd_args
=None, cmd_options
={}, O
=None):
1412 """ Show per-cpu variables
1413 usage: showpcpu [-N <cpu>] [-V] <variable name>
1415 Use -N <cpu> to only dump the value for a given CPU number
1416 Use -V to dump the values of the variables after their addresses
1420 raise ArgumentError("No arguments passed")
1423 ncpu
= kern
.globals.zpercpu_early_count
1424 pcpu_base
= kern
.globals.percpu_base
1426 if "-N" in cmd_options
:
1427 cpu
= unsigned(int(cmd_options
["-N"]))
1428 if cpu
>= unsigned(ncpu
):
1429 raise ArgumentError("Invalid cpu {d}".format(cpu
))
1431 var
= addressof(kern
.GetGlobalVariable('percpu_slot_' + cmd_args
[0]))
1432 ty
= var
.GetSBValue().GetTypeName()
1436 r
= range(cpu
, cpu
+ 1)
1438 def PCPUSlot(pcpu_var
, i
):
1441 addr
= unsigned(pcpu_var
) + unsigned(pcpu_base
.start
) + (i
- 1) * unsigned(pcpu_base
.size
)
1442 return kern
.GetValueFromAddress(addr
, pcpu_var
)
1444 with O
.table("{:<4s} {:<20s}".format("CPU", "address")):
1446 print O
.format("{:<4d} ({:s}){:#x}", i
, ty
, PCPUSlot(var
, i
))
1448 if not "-V" in cmd_options
:
1452 with O
.table("CPU {:d}".format(i
)):
1453 print dereference(PCPUSlot(var
, i
))
1457 def GetBtlogBacktrace(depth
, zstack_record
):
1458 """ Helper routine for getting a BT Log record backtrace stack.
1460 depth:int - The depth of the zstack record
1461 zstack_record:btlog_record_t * - A BTLog record
1463 str - string with backtrace in it.
1467 if not zstack_record
:
1468 return "Zstack record none!"
1470 depth_val
= unsigned(depth
)
1471 while frame
< depth_val
:
1472 frame_pc
= zstack_record
.bt
[frame
]
1473 if not frame_pc
or int(frame_pc
) == 0:
1475 symbol_arr
= kern
.SymbolicateFromAddress(frame_pc
)
1477 symbol_str
= str(symbol_arr
[0].addr
)
1480 out_str
+= "{0: <#0x} <{1: <s}>\n".format(frame_pc
, symbol_str
)
1484 def ShowZStackRecord(zstack_record
, zstack_index
, btrecord_btdepth
, elements_count
):
1485 """ Helper routine for printing a single zstack record
1487 zstack_record:btlog_record_t * - A BTLog record
1488 zstack_index:int - Index for the record in the BTLog table
1493 if zstack_record
.operation
== 1:
1494 out_str
+= "ALLOC. "
1497 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))
1499 print GetBtlogBacktrace(btrecord_btdepth
, zstack_record
)
1502 # Macro: showioalloc
1504 @lldb_command('showioalloc')
1505 def ShowIOAllocations(cmd_args
=None):
1506 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
1507 Routine to display a summary of memory accounting allocated by IOKit allocators.
1509 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_ivars_size
, (kern
.globals.debug_ivars_size
/ 1024))
1510 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_container_malloc_size
, (kern
.globals.debug_container_malloc_size
/ 1024))
1511 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomalloc_size
, (kern
.globals.debug_iomalloc_size
/ 1024))
1512 print "Container allocation = {0: <#0x} = {1: d}K".format(kern
.globals.debug_iomallocpageable_size
, (kern
.globals.debug_iomallocpageable_size
/ 1024))
1515 # EndMacro: showioalloc
1518 # Macro: showselectmem
1519 @lldb_command('showselectmem', "S:")
1520 def ShowSelectMem(cmd_args
=None, cmd_options
={}):
1521 """ Show memory cached by threads on calls to select.
1523 usage: showselectmem [-v]
1524 -v : print each thread's memory
1525 (one line per thread with non-zero select memory)
1526 -S {addr} : Find the thread whose thread-local select set
1527 matches the given address
1531 if config
['verbosity'] > vHUMAN
:
1533 if "-S" in cmd_options
:
1534 opt_wqs
= unsigned(kern
.GetValueFromAddress(cmd_options
["-S"], 'uint64_t *'))
1536 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options
["-S"]))
1539 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1540 for t
in kern
.tasks
:
1541 for th
in IterateQueue(t
.threads
, 'thread *', 'task_threads'):
1542 uth
= Cast(th
.uthread
, 'uthread *');
1544 if hasattr(uth
, 'uu_allocsize'): # old style
1545 thmem
= uth
.uu_allocsize
1547 elif hasattr(uth
, 'uu_wqstate_sz'): # new style
1548 thmem
= uth
.uu_wqstate_sz
1551 print "What kind of uthread is this?!"
1553 if opt_wqs
and opt_wqs
== unsigned(wqs
):
1554 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs
, unsigned(th
), unsigned(th
.thread_id
))
1555 if verbose
and thmem
> 0:
1556 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t
), unsigned(th
.thread_id
), thmem
)
1559 print "Total: {:d} bytes ({:d} kbytes)".format(selmem
, selmem
/1024)
1560 # Endmacro: showselectmem
1563 # Macro: showtaskvme
1564 @lldb_command('showtaskvme', "PS")
1565 def ShowTaskVmeHelper(cmd_args
=None, cmd_options
={}):
1566 """ Display a summary list of the specified vm_map's entries
1567 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
1568 Use -S flag to show VM object shadow chains
1569 Use -P flag to show pager info (mapped file, compressed pages, ...)
1571 show_pager_info
= False
1572 show_all_shadows
= False
1573 if "-P" in cmd_options
:
1574 show_pager_info
= True
1575 if "-S" in cmd_options
:
1576 show_all_shadows
= True
1577 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1578 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1580 @lldb_command('showallvme', "PS")
1581 def ShowAllVME(cmd_args
=None, cmd_options
={}):
1582 """ Routine to print a summary listing of all the vm map entries
1583 Go Through each task in system and show the vm memory regions
1584 Use -S flag to show VM object shadow chains
1585 Use -P flag to show pager info (mapped file, compressed pages, ...)
1587 show_pager_info
= False
1588 show_all_shadows
= False
1589 if "-P" in cmd_options
:
1590 show_pager_info
= True
1591 if "-S" in cmd_options
:
1592 show_all_shadows
= True
1593 for task
in kern
.tasks
:
1594 ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
)
1596 @lldb_command('showallvm')
1597 def ShowAllVM(cmd_args
=None):
1598 """ Routine to print a summary listing of all the vm maps
1600 for task
in kern
.tasks
:
1601 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1602 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1603 print GetVMMapSummary
.header
1604 print GetVMMapSummary(task
.map)
1606 @lldb_command("showtaskvm")
1607 def ShowTaskVM(cmd_args
=None):
1608 """ Display info about the specified task's vm_map
1609 syntax: (lldb) showtaskvm <task_ptr>
1612 print ShowTaskVM
.__doc
__
1614 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
1616 print "Unknown arguments."
1618 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
1619 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
1620 print GetVMMapSummary
.header
1621 print GetVMMapSummary(task
.map)
1624 @lldb_command('showallvmstats')
1625 def ShowAllVMStats(cmd_args
=None):
1626 """ Print a summary of vm statistics in a table format
1628 page_size
= kern
.globals.page_size
1629 vmstats
= lambda:None
1630 vmstats
.wired_count
= 0
1631 vmstats
.resident_count
= 0
1632 vmstats
.resident_max
= 0
1633 vmstats
.internal
= 0
1634 vmstats
.external
= 0
1635 vmstats
.reusable
= 0
1636 vmstats
.compressed
= 0
1637 vmstats
.compressed_peak
= 0
1638 vmstats
.compressed_lifetime
= 0
1641 hdr_format
= "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}"
1642 print hdr_format
.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '')
1643 print hdr_format
.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '')
1644 entry_format
= "{m.hdr.nentries: >6d} {s.wired_count: >10d} {vsize: >10d} {s.resident_count: >10d} {s.new_resident_count: >10d} {s.resident_max: >10d} {s.internal: >10d} {s.external: >10d} {s.reusable: >10d} {s.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {p.p_pid: >10d} {0: <32s} {s.error}"
1646 for task
in kern
.tasks
:
1647 proc
= Cast(task
.bsd_info
, 'proc *')
1648 vmmap
= Cast(task
.map, '_vm_map *')
1650 vmstats
.wired_count
= vmmap
.pmap
.stats
.wired_count
;
1651 vmstats
.resident_count
= unsigned(vmmap
.pmap
.stats
.resident_count
);
1652 vmstats
.resident_max
= vmmap
.pmap
.stats
.resident_max
;
1653 vmstats
.internal
= unsigned(vmmap
.pmap
.stats
.internal
);
1654 vmstats
.external
= unsigned(vmmap
.pmap
.stats
.external
);
1655 vmstats
.reusable
= unsigned(vmmap
.pmap
.stats
.reusable
);
1656 vmstats
.compressed
= unsigned(vmmap
.pmap
.stats
.compressed
);
1657 vmstats
.compressed_peak
= unsigned(vmmap
.pmap
.stats
.compressed_peak
);
1658 vmstats
.compressed_lifetime
= unsigned(vmmap
.pmap
.stats
.compressed_lifetime
);
1659 vmstats
.new_resident_count
= vmstats
.internal
+ vmstats
.external
1661 if vmstats
.internal
< 0:
1662 vmstats
.error
+= '*'
1663 if vmstats
.external
< 0:
1664 vmstats
.error
+= '*'
1665 if vmstats
.reusable
< 0:
1666 vmstats
.error
+= '*'
1667 if vmstats
.compressed
< 0:
1668 vmstats
.error
+= '*'
1669 if vmstats
.compressed_peak
< 0:
1670 vmstats
.error
+= '*'
1671 if vmstats
.compressed_lifetime
< 0:
1672 vmstats
.error
+= '*'
1673 if vmstats
.new_resident_count
+vmstats
.reusable
!= vmstats
.resident_count
:
1674 vmstats
.error
+= '*'
1676 print entry_format
.format(GetProcName(proc
), p
=proc
, m
=vmmap
, vsize
=(unsigned(vmmap
.size
) / page_size
), t
=task
, s
=vmstats
)
1679 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
1680 """ Routine to print out a summary listing of all the entries in a vm_map
1682 task - core.value : a object of type 'task *'
1686 print "vm_map entries for task " + hex(task
)
1687 print GetTaskSummary
.header
1688 print GetTaskSummary(task
)
1690 print "Task {0: <#020x} has map = 0x0"
1692 print GetVMMapSummary
.header
1693 print GetVMMapSummary(task
.map)
1694 vme_list_head
= task
.map.hdr
.links
1695 vme_ptr_type
= GetType('vm_map_entry *')
1696 print GetVMEntrySummary
.header
1697 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1698 print GetVMEntrySummary(vme
, show_pager_info
, show_all_shadows
)
1701 @lldb_command("showmap")
1702 def ShowMap(cmd_args
=None):
1703 """ Routine to print out info about the specified vm_map
1704 usage: showmap <vm_map>
1706 if cmd_args
== None or len(cmd_args
) < 1:
1707 print "Invalid argument.", ShowMap
.__doc
__
1709 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1710 print GetVMMapSummary
.header
1711 print GetVMMapSummary(map_val
)
1713 @lldb_command("showmapvme")
1714 def ShowMapVME(cmd_args
=None):
1715 """Routine to print out info about the specified vm_map and its vm entries
1716 usage: showmapvme <vm_map>
1718 if cmd_args
== None or len(cmd_args
) < 1:
1719 print "Invalid argument.", ShowMapVME
.__doc
__
1721 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1722 print GetVMMapSummary
.header
1723 print GetVMMapSummary(map_val
)
1724 vme_list_head
= map_val
.hdr
.links
1725 vme_ptr_type
= GetType('vm_map_entry *')
1726 print GetVMEntrySummary
.header
1727 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
1728 print GetVMEntrySummary(vme
)
1731 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
1732 @header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s} {7: <7s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free", "pgshift"))
1733 def GetVMMapSummary(vmmap
):
1734 """ Display interesting bits from vm_map struct """
1736 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x} {7: >7d}"
1737 vm_size
= uint64_t(vmmap
.size
).value
1739 if vmmap
.pmap
!= 0: resident_pages
= int(vmmap
.pmap
.stats
.resident_count
)
1741 if int(vmmap
.holelistenabled
) == 0: first_free
= vmmap
.f_s
._first
_free
1742 out_string
+= format_string
.format(vmmap
, vmmap
.pmap
, vm_size
, vmmap
.hdr
.nentries
, resident_pages
, vmmap
.hint
, first_free
, vmmap
.hdr
.page_shift
)
1745 @lldb_type_summary(['vm_map_entry'])
1746 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1747 def GetVMEntrySummary(vme
):
1748 """ Display vm entry specific information. """
1749 page_size
= kern
.globals.page_size
1751 format_string
= "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1752 vme_protection
= int(vme
.protection
)
1753 vme_max_protection
= int(vme
.max_protection
)
1754 vme_extra_info_str
="SC-Ds"[int(vme
.inheritance
)]
1755 if int(vme
.is_sub_map
) != 0 :
1756 vme_extra_info_str
+="s"
1757 elif int(vme
.needs_copy
) != 0 :
1758 vme_extra_info_str
+="n"
1759 num_pages
= (unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)) / page_size
1760 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
)
1763 # EndMacro: showtaskvme
1764 @lldb_command('showmapwired')
1765 def ShowMapWired(cmd_args
=None):
1766 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1768 if cmd_args
== None or len(cmd_args
) < 1:
1769 print "Invalid argument", ShowMapWired
.__doc
__
1771 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
1774 @lldb_type_summary(['kmod_info_t *'])
1775 @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'))
1776 def GetKextSummary(kmod
):
1777 """ returns a string representation of kext information
1780 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1781 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod
.address
), unsigned(kmod
.size
))
1782 text_segment
= macho
.get_text_segment(segments
)
1783 if not text_segment
:
1784 text_segment
= segments
[0]
1785 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
)
1788 @lldb_type_summary(['uuid_t'])
1790 def GetUUIDSummary(uuid
):
1791 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1793 arr
= Cast(addressof(uuid
), 'uint8_t *')
1796 data
.append(int(arr
[i
]))
1797 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
)
1799 @lldb_command('showallkmods')
1800 def ShowAllKexts(cmd_args
=None):
1801 """Display a summary listing of all loaded kexts (alias: showallkmods)
1803 kmod_val
= kern
.globals.kmod
1804 kextuuidinfo
= GetKextLoadInformation(show_progress
=(config
['verbosity'] > vHUMAN
))
1805 print "{: <36s} ".format("UUID") + GetKextSummary
.header
1806 for kval
in IterateLinkedList(kmod_val
, 'next'):
1807 uuid
= "........-....-....-....-............"
1808 kaddr
= unsigned(kval
.address
)
1809 found_kext_summary
= None
1810 for l
in kextuuidinfo
:
1811 if kaddr
== int(l
[3],16):
1813 found_kext_summary
= l
1815 if found_kext_summary
:
1816 _ksummary
= GetKextSummary(found_kext_summary
[7])
1818 _ksummary
= GetKextSummary(kval
)
1819 print uuid
+ " " + _ksummary
1821 def GetKmodWithAddr(addr
):
1822 """ Go through kmod list and find one with begin_addr as addr
1823 returns: None if not found. else a cvalue of type kmod
1825 kmod_val
= kern
.globals.kmod
1826 for kval
in IterateLinkedList(kmod_val
, 'next'):
1827 if addr
== unsigned(kval
.address
):
1831 def GetAllSegmentsAndSectionsFromDataInMemory(address
, size
):
1832 """ reads memory at address and parses mach_header to get segment and section information
1833 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
1834 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
1835 if TEXT segment is not found a dummy segment & section with address, size is returned.
1837 cache_hash
= "kern.kexts.segments.{}.{}".format(address
, size
)
1838 cached_result
= caching
.GetDynamicCacheData(cache_hash
,())
1840 return cached_result
1842 defval
= macho
.MachOSegment('__TEXT', address
, size
, 0, size
)
1843 if address
== 0 or size
== 0:
1844 return ([defval
], [defval
])
1846 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
1847 # until we have separate version. we will pay penalty only on arm64 devices
1848 if not kern
.arch
.startswith('arm64'):
1849 return ([defval
], [defval
])
1851 restrict_size_to_read
= 1536
1853 while machoObject
is None:
1854 err
= lldb
.SBError()
1855 size_to_read
= min(size
, restrict_size_to_read
)
1856 data
= LazyTarget
.GetProcess().ReadMemory(address
, size_to_read
, err
)
1857 if not err
.Success():
1858 print "Failed to read memory at {} and size {}".format(address
, size_to_read
)
1859 return ([defval
], [defval
])
1861 m
= macho
.MemMacho(data
, len(data
))
1863 except Exception as e
:
1864 if str(e
.message
).find('unpack requires a string argument') >= 0:
1865 # this may be due to short read of memory. Lets do double read size.
1866 restrict_size_to_read
*= 2
1867 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read
))
1870 print "Failed to read MachO for address {} errormessage: {}".format(address
, e
.message
)
1871 return ([defval
], [defval
])
1872 # end of while loop. We have machoObject defined
1873 segments
= machoObject
.get_segments_with_name('')
1874 sections
= machoObject
.get_sections_with_name('')
1875 rval
= (segments
, sections
)
1876 caching
.SaveDynamicCacheData(cache_hash
, rval
)
1879 def GetKextLoadInformation(addr
=0, show_progress
=False):
1880 """ Extract the kext uuid and load address information from the kernel data structure.
1882 addr - int - optional integer that is the address to search for.
1884 [] - array with each entry of format
1885 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
1886 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
1888 cached_result
= caching
.GetDynamicCacheData("kern.kexts.loadinformation", [])
1889 ## if specific addr is provided then ignore caching
1890 if cached_result
and not addr
:
1891 return cached_result
1893 # because of <rdar://problem/12683084>, we can't find summaries directly
1894 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1895 baseaddr
= unsigned(kern
.globals.gLoadedKextSummaries
) + 0x10
1896 summaries_begin
= kern
.GetValueFromAddress(baseaddr
, 'OSKextLoadedKextSummary *')
1897 total_summaries
= int(kern
.globals.gLoadedKextSummaries
.numSummaries
)
1898 kext_version
= int(kern
.globals.gLoadedKextSummaries
.version
)
1899 entry_size
= 64 + 16 + 8 + 8 + 8 + 4 + 4
1900 if kext_version
>= 2 :
1901 entry_size
= int(kern
.globals.gLoadedKextSummaries
.entry_size
)
1903 for i
in range(total_summaries
):
1905 print "progress: {}/{}".format(i
, total_summaries
)
1906 tmpaddress
= unsigned(summaries_begin
) + (i
* entry_size
)
1907 current_kext
= kern
.GetValueFromAddress(tmpaddress
, 'OSKextLoadedKextSummary *')
1908 # code to extract macho information
1909 segments
, sections
= GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext
.address
), unsigned(current_kext
.size
))
1910 seginfo
= macho
.get_text_segment(segments
)
1912 seginfo
= segments
[0]
1913 kmod_obj
= GetKmodWithAddr(unsigned(current_kext
.address
))
1915 if addr
== unsigned(current_kext
.address
) or addr
== seginfo
.vmaddr
:
1916 return [(GetUUIDSummary(current_kext
.uuid
) , hex(seginfo
.vmaddr
).rstrip('L'), str(current_kext
.name
), hex(current_kext
.address
), segments
, seginfo
, current_kext
, kmod_obj
)]
1917 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
))
1920 caching
.SaveDynamicCacheData("kern.kexts.loadinformation", retval
)
1923 lldb_alias('showallkexts', 'showallkmods')
1925 def GetOSKextVersion(version_num
):
1926 """ returns a string of format 1.2.3x from the version_num
1927 params: version_num - int
1930 if version_num
== -1 :
1932 (MAJ_MULT
, MIN_MULT
, REV_MULT
,STAGE_MULT
) = (100000000, 1000000, 10000, 1000)
1933 version
= version_num
1935 vers_major
= version
/ MAJ_MULT
1936 version
= version
- (vers_major
* MAJ_MULT
)
1938 vers_minor
= version
/ MIN_MULT
1939 version
= version
- (vers_minor
* MIN_MULT
)
1941 vers_revision
= version
/ REV_MULT
1942 version
= version
- (vers_revision
* REV_MULT
)
1944 vers_stage
= version
/ STAGE_MULT
1945 version
= version
- (vers_stage
* STAGE_MULT
)
1947 vers_stage_level
= version
1949 out_str
= "%d.%d" % (vers_major
, vers_minor
)
1950 if vers_revision
> 0: out_str
+= ".%d" % vers_revision
1951 if vers_stage
== 1 : out_str
+= "d%d" % vers_stage_level
1952 if vers_stage
== 3 : out_str
+= "a%d" % vers_stage_level
1953 if vers_stage
== 5 : out_str
+= "b%d" % vers_stage_level
1954 if vers_stage
== 6 : out_str
+= "fc%d" % vers_stage_level
1958 @lldb_command('showallknownkmods')
1959 def ShowAllKnownKexts(cmd_args
=None):
1960 """ Display a summary listing of all kexts known in the system.
1961 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1963 kext_count
= int(kern
.globals.sKextsByID
.count
)
1965 kext_dictionary
= kern
.globals.sKextsByID
.dictionary
1966 print "%d kexts in sKextsByID:" % kext_count
1967 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1968 format_string
= "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1970 while index
< kext_count
:
1971 kext_dict
= GetObjectAtIndexFromArray(kext_dictionary
, index
)
1972 kext_name
= str(kext_dict
.key
.string
)
1973 osk
= Cast(kext_dict
.value
, 'OSKext *')
1974 if int(osk
.flags
.loaded
) :
1975 load_addr
= "{0: <#020x}".format(osk
.kmod_info
)
1976 id = "{0: >5d}".format(osk
.loadTag
)
1978 load_addr
= "------"
1980 version_num
= unsigned(osk
.version
)
1981 version
= GetOSKextVersion(version_num
)
1982 print format_string
.format(osk
, load_addr
, id, version
, kext_name
)
1987 def FindKmodNameForAddr(addr
):
1988 """ Given an address, return the name of the kext containing that address
1990 addr
= unsigned(addr
)
1991 all_kexts_info
= GetKextLoadInformation()
1992 for kinfo
in all_kexts_info
:
1993 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
1995 return kinfo
[7].name
1999 @lldb_command('addkextaddr')
2000 def AddKextAddr(cmd_args
=[]):
2001 """ Given an address, load the kext which contains that address
2002 Syntax: (lldb) addkextaddr <addr>
2004 if len(cmd_args
) < 1:
2005 raise ArgumentError("Insufficient arguments")
2007 addr
= ArgumentStringToInt(cmd_args
[0])
2008 all_kexts_info
= GetKextLoadInformation()
2009 kernel_uuid
= str(kern
.globals.kernel_uuid_string
).lower()
2011 found_segment
= None
2012 for kinfo
in all_kexts_info
:
2013 segment
= macho
.get_segment_with_addr(kinfo
[4], addr
)
2015 print GetKextSummary
.header
2016 print GetKextSummary(kinfo
[7]) + " segment: {} offset = {:#0x}".format(segment
.name
, (addr
- segment
.vmaddr
))
2017 cur_uuid
= kinfo
[0].lower()
2018 if (kernel_uuid
== cur_uuid
):
2021 print "Fetching dSYM for %s" % cur_uuid
2022 info
= dsymForUUID(cur_uuid
)
2023 if info
and 'DBGSymbolRichExecutable' in info
:
2024 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
2025 addDSYM(cur_uuid
, info
)
2026 loadDSYM(cur_uuid
, int(kinfo
[1],16), kinfo
[4])
2028 print "Failed to get symbol info for %s" % cur_uuid
2032 @lldb_command('showkmodaddr')
2033 def ShowKmodAddr(cmd_args
=[]):
2034 """ Given an address, print the offset and name for the kmod containing it
2035 Syntax: (lldb) showkmodaddr <addr>
2037 if len(cmd_args
) < 1:
2038 raise ArgumentError("Insufficient arguments")
2040 addr
= ArgumentStringToInt(cmd_args
[0])
2041 all_kexts_info
= GetKextLoadInformation()
2043 found_segment
= None
2044 for kinfo
in all_kexts_info
:
2045 s
= macho
.get_segment_with_addr(kinfo
[4], addr
)
2051 print GetKextSummary
.header
2052 print GetKextSummary(found_kinfo
[7]) + " segment: {} offset = {:#0x}".format(found_segment
.name
, (addr
- found_segment
.vmaddr
))
2057 @lldb_command('addkext','AF:N:')
2058 def AddKextSyms(cmd_args
=[], cmd_options
={}):
2059 """ Add kext symbols into lldb.
2060 This command finds symbols for a uuid and load the required executable
2062 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
2063 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
2064 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
2065 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
2066 addkext all : Will load all the kext symbols - SLOW
2070 if "-F" in cmd_options
:
2071 exec_path
= cmd_options
["-F"]
2072 exec_full_path
= ResolveFSPath(exec_path
)
2073 if not os
.path
.exists(exec_full_path
):
2074 raise ArgumentError("Unable to resolve {:s}".format(exec_path
))
2076 if not os
.path
.isfile(exec_full_path
):
2077 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
2078 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
2079 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path
))
2084 slide_value
= cmd_args
[0]
2085 debuglog("loading slide value from user input %s" % cmd_args
[0])
2087 filespec
= lldb
.SBFileSpec(exec_full_path
, False)
2088 print "target modules add %s" % exec_full_path
2089 print lldb_run_command("target modules add %s" % exec_full_path
)
2090 loaded_module
= LazyTarget
.GetTarget().FindModule(filespec
)
2091 if loaded_module
.IsValid():
2092 uuid_str
= loaded_module
.GetUUIDString()
2093 debuglog("added module %s with uuid %s" % (exec_full_path
, uuid_str
))
2094 if slide_value
is None:
2095 all_kexts_info
= GetKextLoadInformation()
2096 for k
in all_kexts_info
:
2098 if k
[0].lower() == uuid_str
.lower():
2101 debuglog("found the slide %s for uuid %s" % (k
[1], k
[0]))
2102 if slide_value
is None:
2103 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path
)
2106 cmd_str
= "target modules load --file %s --slide %s" % ( exec_full_path
, str(slide_value
))
2109 cmd_str
= "target modules load --file {} ".format(exec_full_path
)
2112 sections_str
+= " {} {:#0x} ".format(s
.name
, s
.vmaddr
)
2113 cmd_str
+= sections_str
2116 lldb
.debugger
.HandleCommand(cmd_str
)
2118 kern
.symbolicator
= None
2121 all_kexts_info
= GetKextLoadInformation()
2122 kernel_uuid
= str(kern
.globals.kernel_uuid_string
).lower()
2124 if "-N" in cmd_options
:
2125 kext_name
= cmd_options
["-N"]
2126 kext_name_matches
= GetLongestMatchOption(kext_name
, [str(x
[2]) for x
in all_kexts_info
], True)
2127 if len(kext_name_matches
) != 1 and "-A" not in cmd_options
:
2128 print "Ambiguous match for name: {:s}".format(kext_name
)
2129 if len(kext_name_matches
) > 0:
2130 print "Options are:\n\t" + "\n\t".join(kext_name_matches
)
2132 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches
[0], kext_name
))
2133 for cur_knm
in kext_name_matches
:
2134 for x
in all_kexts_info
:
2136 cur_uuid
= x
[0].lower()
2137 if (kernel_uuid
== cur_uuid
):
2140 print "Fetching dSYM for {:s}".format(cur_uuid
)
2141 info
= dsymForUUID(cur_uuid
)
2142 if info
and 'DBGSymbolRichExecutable' in info
:
2143 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid
, info
['DBGSymbolRichExecutable'])
2144 addDSYM(cur_uuid
, info
)
2145 loadDSYM(cur_uuid
, int(x
[1],16), x
[4])
2147 print "Failed to get symbol info for {:s}".format(cur_uuid
)
2149 kern
.symbolicator
= None
2152 if len(cmd_args
) < 1:
2153 raise ArgumentError("No arguments specified.")
2155 uuid
= cmd_args
[0].lower()
2157 load_all_kexts
= False
2159 load_all_kexts
= True
2161 if not load_all_kexts
and len(uuid_regex
.findall(uuid
)) == 0:
2162 raise ArgumentError("Unknown argument {:s}".format(uuid
))
2164 for k_info
in all_kexts_info
:
2165 cur_uuid
= k_info
[0].lower()
2166 if load_all_kexts
or (uuid
== cur_uuid
):
2167 if (kernel_uuid
!= cur_uuid
):
2168 print "Fetching dSYM for %s" % cur_uuid
2169 info
= dsymForUUID(cur_uuid
)
2170 if info
and 'DBGSymbolRichExecutable' in info
:
2171 print "Adding dSYM (%s) for %s" % (cur_uuid
, info
['DBGSymbolRichExecutable'])
2172 addDSYM(cur_uuid
, info
)
2173 loadDSYM(cur_uuid
, int(k_info
[1],16), k_info
[4])
2175 print "Failed to get symbol info for %s" % cur_uuid
2177 kern
.symbolicator
= None
2182 lldb_alias('showkmod', 'showkmodaddr')
2183 lldb_alias('showkext', 'showkmodaddr')
2184 lldb_alias('showkextaddr', 'showkmodaddr')
2186 @lldb_type_summary(['mount *'])
2187 @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'))
2188 def GetMountSummary(mount
):
2189 """ Display a summary of mount on the system
2191 out_string
= ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
2192 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
2193 "{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'))
2196 @lldb_command('showallmounts')
2197 def ShowAllMounts(cmd_args
=None):
2198 """ Print all mount points
2200 mntlist
= kern
.globals.mountlist
2201 print GetMountSummary
.header
2202 for mnt
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
2203 print GetMountSummary(mnt
)
2206 lldb_alias('ShowAllVols', 'showallmounts')
2208 @lldb_command('systemlog')
2209 def ShowSystemLog(cmd_args
=None):
2210 """ Display the kernel's printf ring buffer """
2211 msgbufp
= kern
.globals.msgbufp
2212 msg_size
= int(msgbufp
.msg_size
)
2213 msg_bufx
= int(msgbufp
.msg_bufx
)
2214 msg_bufr
= int(msgbufp
.msg_bufr
)
2215 msg_bufc
= msgbufp
.msg_bufc
2216 msg_bufc_data
= msg_bufc
.GetSBValue().GetPointeeData(0, msg_size
)
2218 # the buffer is circular; start at the write pointer to end,
2219 # then from beginning to write pointer
2221 err
= lldb
.SBError()
2222 for i
in range(msg_bufx
, msg_size
) + range(0, msg_bufx
) :
2224 cbyte
= msg_bufc_data
.GetUnsignedInt8(err
, i
)
2225 if not err
.Success() :
2226 raise ValueError("Failed to read character at offset " + str(i
) + ": " + err
.GetCString())
2241 @static_var('output','')
2242 def _GetVnodePathName(vnode
, vnodename
):
2243 """ Internal function to get vnode path string from vnode structure.
2247 returns Nothing. The output will be stored in the static variable.
2251 if int(vnode
.v_flag
) & 0x1 and int(hex(vnode
.v_mount
), 16) !=0:
2252 if int(vnode
.v_mount
.mnt_vnodecovered
):
2253 _GetVnodePathName(vnode
.v_mount
.mnt_vnodecovered
, str(vnode
.v_mount
.mnt_vnodecovered
.v_name
) )
2255 _GetVnodePathName(vnode
.v_parent
, str(vnode
.v_parent
.v_name
))
2256 _GetVnodePathName
.output
+= "/%s" % vnodename
2258 def GetVnodePath(vnode
):
2259 """ Get string representation of the vnode
2260 params: vnodeval - value representing vnode * in the kernel
2261 return: str - of format /path/to/something
2265 if (int(vnode
.v_flag
) & 0x000001) and int(hex(vnode
.v_mount
), 16) != 0 and (int(vnode
.v_mount
.mnt_flag
) & 0x00004000) :
2268 _GetVnodePathName
.output
= ''
2269 if abs(vnode
.v_name
) != 0:
2270 _GetVnodePathName(vnode
, str(vnode
.v_name
))
2271 out_str
+= _GetVnodePathName
.output
2273 out_str
+= 'v_name = NULL'
2274 _GetVnodePathName
.output
= ''
2278 @lldb_command('showvnodepath')
2279 def ShowVnodePath(cmd_args
=None):
2280 """ Prints the path for a vnode
2281 usage: showvnodepath <vnode>
2283 if cmd_args
!= None and len(cmd_args
) > 0 :
2284 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
2286 print GetVnodePath(vnode_val
)
2289 # Macro: showvnodedev
2290 def GetVnodeDevInfo(vnode
):
2291 """ Internal function to get information from the device type vnodes
2292 params: vnode - value representing struct vnode *
2293 return: str - formatted output information for block and char vnode types passed as param
2295 vnodedev_output
= ""
2296 vblk_type
= GetEnumValue('vtype::VBLK')
2297 vchr_type
= GetEnumValue('vtype::VCHR')
2298 if (vnode
.v_type
== vblk_type
) or (vnode
.v_type
== vchr_type
):
2299 devnode
= Cast(vnode
.v_data
, 'devnode_t *')
2300 devnode_dev
= devnode
.dn_typeinfo
.dev
2301 devnode_major
= (devnode_dev
>> 24) & 0xff
2302 devnode_minor
= devnode_dev
& 0x00ffffff
2304 # boilerplate device information for a vnode
2305 vnodedev_output
+= "Device Info:\n\t vnode:\t\t{:#x}".format(vnode
)
2306 vnodedev_output
+= "\n\t type:\t\t"
2307 if (vnode
.v_type
== vblk_type
):
2308 vnodedev_output
+= "VBLK"
2309 if (vnode
.v_type
== vchr_type
):
2310 vnodedev_output
+= "VCHR"
2311 vnodedev_output
+= "\n\t name:\t\t{:<s}".format(vnode
.v_name
)
2312 vnodedev_output
+= "\n\t major, minor:\t{:d},{:d}".format(devnode_major
, devnode_minor
)
2313 vnodedev_output
+= "\n\t mode\t\t0{:o}".format(unsigned(devnode
.dn_mode
))
2314 vnodedev_output
+= "\n\t owner (u,g):\t{:d} {:d}".format(devnode
.dn_uid
, devnode
.dn_gid
)
2316 # decode device specific data
2317 vnodedev_output
+= "\nDevice Specific Information:\t"
2318 if (vnode
.v_type
== vblk_type
):
2319 vnodedev_output
+= "Sorry, I do not know how to decode block devices yet!"
2320 vnodedev_output
+= "\nMaybe you can write me!"
2322 if (vnode
.v_type
== vchr_type
):
2323 # Device information; this is scanty
2325 if (devnode_major
> 42) or (devnode_major
< 0):
2326 vnodedev_output
+= "Invalid major #\n"
2327 # static assignments in conf
2328 elif (devnode_major
== 0):
2329 vnodedev_output
+= "Console mux device\n"
2330 elif (devnode_major
== 2):
2331 vnodedev_output
+= "Current tty alias\n"
2332 elif (devnode_major
== 3):
2333 vnodedev_output
+= "NULL device\n"
2334 elif (devnode_major
== 4):
2335 vnodedev_output
+= "Old pty slave\n"
2336 elif (devnode_major
== 5):
2337 vnodedev_output
+= "Old pty master\n"
2338 elif (devnode_major
== 6):
2339 vnodedev_output
+= "Kernel log\n"
2340 elif (devnode_major
== 12):
2341 vnodedev_output
+= "Memory devices\n"
2342 # Statically linked dynamic assignments
2343 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptmx_open')):
2344 vnodedev_output
+= "Cloning pty master not done\n"
2345 #GetVnodeDevCpty(devnode_major, devnode_minor)
2346 elif unsigned(kern
.globals.cdevsw
[devnode_major
].d_open
) == unsigned(kern
.GetLoadAddressForSymbol('ptsd_open')):
2347 vnodedev_output
+= "Cloning pty slave not done\n"
2348 #GetVnodeDevCpty(devnode_major, devnode_minor)
2350 vnodedev_output
+= "RESERVED SLOT\n"
2352 vnodedev_output
+= "{:#x} is not a device".format(vnode
)
2353 return vnodedev_output
2355 @lldb_command('showvnodedev')
2356 def ShowVnodeDev(cmd_args
=None):
2357 """ Routine to display details of all vnodes of block and character device types
2358 Usage: showvnodedev <address of vnode>
2361 print "No arguments passed"
2362 print ShowVnodeDev
.__doc
__
2364 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
2366 print "unknown arguments:", str(cmd_args
)
2368 print GetVnodeDevInfo(vnode_val
)
2370 # EndMacro: showvnodedev
2372 # Macro: showvnodelocks
2373 def GetVnodeLock(lockf
):
2374 """ Internal function to get information from the given advisory lock
2375 params: lockf - value representing v_lockf member in struct vnode *
2376 return: str - formatted output information for the advisory lock
2378 vnode_lock_output
= ''
2379 lockf_flags
= lockf
.lf_flags
2380 lockf_type
= lockf
.lf_type
2381 if lockf_flags
& 0x20:
2382 vnode_lock_output
+= ("{: <8s}").format('flock')
2383 if lockf_flags
& 0x40:
2384 vnode_lock_output
+= ("{: <8s}").format('posix')
2385 if lockf_flags
& 0x80:
2386 vnode_lock_output
+= ("{: <8s}").format('prov')
2387 if lockf_flags
& 0x10:
2388 vnode_lock_output
+= ("{: <4s}").format('W')
2389 if lockf_flags
& 0x400:
2390 vnode_lock_output
+= ("{: <8s}").format('ofd')
2392 vnode_lock_output
+= ("{: <4s}").format('.')
2394 # POSIX file vs advisory range locks
2395 if lockf_flags
& 0x40:
2396 lockf_proc
= Cast(lockf
.lf_id
, 'proc *')
2397 vnode_lock_output
+= ("PID {: <18d}").format(lockf_proc
.p_pid
)
2399 vnode_lock_output
+= ("ID {: <#019x}").format(int(lockf
.lf_id
))
2403 vnode_lock_output
+= ("{: <12s}").format('shared')
2406 vnode_lock_output
+= ("{: <12s}").format('exclusive')
2409 vnode_lock_output
+= ("{: <12s}").format('unlock')
2411 vnode_lock_output
+= ("{: <12s}").format('unknown')
2413 # start and stop values
2414 vnode_lock_output
+= ("{: #018x} ..").format(lockf
.lf_start
)
2415 vnode_lock_output
+= ("{: #018x}\n").format(lockf
.lf_end
)
2416 return vnode_lock_output
2418 @header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
2419 def GetVnodeLocksSummary(vnode
):
2420 """ Internal function to get summary of advisory locks for the given vnode
2421 params: vnode - value representing the vnode object
2422 return: str - formatted output information for the summary of advisory locks
2426 lockf_list
= vnode
.v_lockf
2427 for lockf_itr
in IterateLinkedList(lockf_list
, 'lf_next'):
2428 out_str
+= ("{: <4s}").format('H')
2429 out_str
+= GetVnodeLock(lockf_itr
)
2430 lockf_blocker
= lockf_itr
.lf_blkhd
.tqh_first
2431 while lockf_blocker
:
2432 out_str
+= ("{: <4s}").format('>')
2433 out_str
+= GetVnodeLock(lockf_blocker
)
2434 lockf_blocker
= lockf_blocker
.lf_block
.tqe_next
2437 @lldb_command('showvnodelocks')
2438 def ShowVnodeLocks(cmd_args
=None):
2439 """ Routine to display list of advisory record locks for the given vnode address
2440 Usage: showvnodelocks <address of vnode>
2443 print "No arguments passed"
2444 print ShowVnodeLocks
.__doc
__
2446 vnode_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
2448 print "unknown arguments:", str(cmd_args
)
2450 print GetVnodeLocksSummary
.header
2451 print GetVnodeLocksSummary(vnode_val
)
2453 # EndMacro: showvnodelocks
2455 # Macro: showproclocks
2457 @lldb_command('showproclocks')
2458 def ShowProcLocks(cmd_args
=None):
2459 """ Routine to display list of advisory record locks for the given process
2460 Usage: showproclocks <address of proc>
2463 print "No arguments passed"
2464 print ShowProcLocks
.__doc
__
2466 proc
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
2468 print "unknown arguments:", str(cmd_args
)
2471 proc_filedesc
= proc
.p_fd
2472 fd_lastfile
= proc_filedesc
.fd_lastfile
2473 fd_ofiles
= proc_filedesc
.fd_ofiles
2476 while count
<= fd_lastfile
:
2477 if fd_ofiles
[count
]:
2478 fglob
= fd_ofiles
[count
].fp_glob
2479 fo_type
= fglob
.fg_ops
.fo_type
2481 fg_data
= fglob
.fg_data
2482 fg_vnode
= Cast(fg_data
, 'vnode *')
2483 name
= fg_vnode
.v_name
2484 lockf_itr
= fg_vnode
.v_lockf
2487 print GetVnodeLocksSummary
.header
2489 out_str
+= ("\n( fd {:d}, name ").format(count
)
2491 out_str
+= "(null) )\n"
2493 out_str
+= "{:s} )\n".format(name
)
2495 print GetVnodeLocksSummary(fg_vnode
)
2497 print "\n{0: d} total locks for {1: #018x}".format(seen
, proc
)
2499 # EndMacro: showproclocks
2501 @lldb_type_summary(['vnode_t', 'vnode *'])
2502 @header("{0: <20s} {1: >8s} {2: >9s} {3: >8s} {4: <20s} {5: <6s} {6: <20s} {7: <6s} {8: <6s} {9: <35s}".format('vnode', 'usecount', 'kusecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name'))
2503 def GetVnodeSummary(vnode
):
2504 """ Get a summary of important information out of vnode
2507 format_string
= "{0: <#020x} {1: >8d} {2: >8d} {3: >8d} {4: <#020x} {5: <6s} {6: <#020x} {7: <6s} {8: <6s} {9: <35s}"
2508 usecount
= int(vnode
.v_usecount
)
2509 kusecount
= int(vnode
.v_kusecount
)
2510 iocount
= int(vnode
.v_iocount
)
2511 v_data_ptr
= int(hex(vnode
.v_data
), 16)
2512 vtype
= int(vnode
.v_type
)
2513 vtype_str
= "%d" % vtype
2514 vnode_types
= ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
2515 if vtype
>= 0 and vtype
< len(vnode_types
):
2516 vtype_str
= vnode_types
[vtype
]
2517 parent_ptr
= int(hex(vnode
.v_parent
), 16)
2518 name_ptr
= int(hex(vnode
.v_name
), 16)
2521 name
= str(vnode
.v_name
)
2522 elif int(vnode
.v_tag
) == 16 :
2523 cnode
= Cast(vnode
.v_data
, 'cnode *')
2524 name
= "hfs: %s" % str( Cast(cnode
.c_desc
.cd_nameptr
, 'char *'))
2526 csblob_version
= '-'
2527 if (vtype
== 1) and (vnode
.v_un
.vu_ubcinfo
!= 0):
2528 csblob_version
= '{: <6d}'.format(vnode
.v_un
.vu_ubcinfo
.cs_add_gen
)
2529 # Check to see if vnode is mapped/unmapped
2530 if (vnode
.v_un
.vu_ubcinfo
.ui_flags
& 0x8) != 0:
2534 out_str
+= format_string
.format(vnode
, usecount
, kusecount
, iocount
, v_data_ptr
, vtype_str
, parent_ptr
, mapped
, csblob_version
, name
)
2537 @lldb_command('showallvnodes')
2538 def ShowAllVnodes(cmd_args
=None):
2539 """ Display info about all vnodes
2541 mntlist
= kern
.globals.mountlist
2542 print GetVnodeSummary
.header
2543 for mntval
in IterateTAILQ_HEAD(mntlist
, 'mnt_list'):
2544 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2545 print GetVnodeSummary(vnodeval
)
2548 @lldb_command('showvnode')
2549 def ShowVnode(cmd_args
=None):
2550 """ Display info about one vnode
2551 usage: showvnode <vnode>
2553 if cmd_args
== None or len(cmd_args
) < 1:
2554 print "Please provide valid vnode argument. Type help showvnode for help."
2556 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0],'vnode *')
2557 print GetVnodeSummary
.header
2558 print GetVnodeSummary(vnodeval
)
2560 @lldb_command('showvolvnodes')
2561 def ShowVolVnodes(cmd_args
=None):
2562 """ Display info about all vnodes of a given mount_t
2564 if cmd_args
== None or len(cmd_args
) < 1:
2565 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2567 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2568 print GetVnodeSummary
.header
2569 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2570 print GetVnodeSummary(vnodeval
)
2573 @lldb_command('showvolbusyvnodes')
2574 def ShowVolBusyVnodes(cmd_args
=None):
2575 """ Display info about busy (iocount!=0) vnodes of a given mount_t
2577 if cmd_args
== None or len(cmd_args
) < 1:
2578 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2580 mntval
= kern
.GetValueFromAddress(cmd_args
[0], 'mount_t')
2581 print GetVnodeSummary
.header
2582 for vnodeval
in IterateTAILQ_HEAD(mntval
.mnt_vnodelist
, 'v_mntvnodes'):
2583 if int(vnodeval
.v_iocount
) != 0:
2584 print GetVnodeSummary(vnodeval
)
2586 @lldb_command('showallbusyvnodes')
2587 def ShowAllBusyVnodes(cmd_args
=None):
2588 """ Display info about all busy (iocount!=0) vnodes
2590 mntlistval
= kern
.globals.mountlist
2591 for mntval
in IterateTAILQ_HEAD(mntlistval
, 'mnt_list'):
2592 ShowVolBusyVnodes([hex(mntval
)])
2594 @lldb_command('print_vnode')
2595 def PrintVnode(cmd_args
=None):
2596 """ Prints out the fields of a vnode struct
2597 Usage: print_vnode <vnode>
2600 print "Please provide valid vnode argument. Type help print_vnode for help."
2604 @lldb_command('showworkqvnodes')
2605 def ShowWorkqVnodes(cmd_args
=None):
2606 """ Print the vnode worker list
2607 Usage: showworkqvnodes <struct mount *>
2610 print "Please provide valid mount argument. Type help showworkqvnodes for help."
2613 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2614 vp
= Cast(mp
.mnt_workerqueue
.tqh_first
, 'vnode *')
2615 print GetVnodeSummary
.header
2617 print GetVnodeSummary(vp
)
2618 vp
= vp
.v_mntvnodes
.tqe_next
2620 @lldb_command('shownewvnodes')
2621 def ShowNewVnodes(cmd_args
=None):
2622 """ Print the new vnode list
2623 Usage: shownewvnodes <struct mount *>
2626 print "Please provide valid mount argument. Type help shownewvnodes for help."
2628 mp
= kern
.GetValueFromAddress(cmd_args
[0], 'mount *')
2629 vp
= Cast(mp
.mnt_newvnodes
.tqh_first
, 'vnode *')
2630 print GetVnodeSummary
.header
2632 print GetVnodeSummary(vp
)
2633 vp
= vp
.v_mntvnodes
.tqe_next
2636 @lldb_command('showprocvnodes')
2637 def ShowProcVnodes(cmd_args
=None):
2638 """ Routine to print out all the open fds which are vnodes in a process
2639 Usage: showprocvnodes <proc *>
2642 print "Please provide valid proc argument. Type help showprocvnodes for help."
2644 procptr
= kern
.GetValueFromAddress(cmd_args
[0], 'proc *')
2645 fdptr
= Cast(procptr
.p_fd
, 'filedesc *')
2646 if int(fdptr
.fd_cdir
) != 0:
2647 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_cdir
))
2648 if int(fdptr
.fd_rdir
) != 0:
2649 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary
.header
, GetVnodeSummary(fdptr
.fd_rdir
))
2651 print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary
.header
2652 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
2653 fpptr
= Cast(fdptr
.fd_ofiles
, 'uint64_t *')
2654 while count
< fdptr
.fd_nfiles
:
2655 fpp
= dereference(fpptr
)
2656 fproc
= kern
.GetValueFromAddress(int(fpp
), 'fileproc *')
2658 fglob
= dereference(fproc
).fp_glob
2660 if (int(fglob
) != 0) and (int(fglob
.fg_ops
.fo_type
) == 1):
2661 if (fdptr
.fd_ofileflags
[count
] & 1): flags
+= 'E'
2662 if (fdptr
.fd_ofileflags
[count
] & 2): flags
+= 'F'
2663 if (fdptr
.fd_ofileflags
[count
] & 4): flags
+= 'R'
2664 if (fdptr
.fd_ofileflags
[count
] & 8): flags
+= 'C'
2665 print '{0: <5d} {1: <7s} {2: <#020x} '.format(count
, flags
, fglob
) + GetVnodeSummary(Cast(fglob
.fg_data
, 'vnode *'))
2667 fpptr
= kern
.GetValueFromAddress(int(fpptr
) + kern
.ptrsize
,'uint64_t *')
2669 @lldb_command('showallprocvnodes')
2670 def ShowAllProcVnodes(cmd_args
=None):
2671 """ Routine to print out all the open fds which are vnodes
2674 procptr
= Cast(kern
.globals.allproc
.lh_first
, 'proc *')
2675 while procptr
and int(procptr
) != 0:
2676 print '{:<s}'.format("=" * 106)
2677 print GetProcInfo(procptr
)
2678 ShowProcVnodes([int(procptr
)])
2679 procptr
= procptr
.p_list
.le_next
2681 @xnudebug_test('test_vnode')
2682 def TestShowAllVnodes(kernel_target
, config
, lldb_obj
, isConnected
):
2683 """ Test the functionality of vnode related commands
2689 print "Target is not connected. Cannot test memstats"
2691 res
= lldb
.SBCommandReturnObject()
2692 lldb_obj
.debugger
.GetCommandInterpreter().HandleCommand("showallvnodes", res
)
2693 result
= res
.GetOutput()
2694 if len(result
.split("\n")) > 2 and result
.find('VREG') != -1 and len(result
.splitlines()[2].split()) > 5:
2700 @lldb_type_summary(['_lck_grp_ *'])
2701 def GetMutexEntry(mtxg
):
2702 """ Summarize a mutex group entry with important information.
2704 mtxg: value - obj representing a mutex group in kernel
2706 out_string - summary of the mutex group
2710 if kern
.ptrsize
== 8:
2711 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2713 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2715 if mtxg
.lck_grp_mtxcnt
:
2716 out_string
+= format_string
.format(mtxg
, mtxg
.lck_grp_mtxcnt
,mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_util_cnt
,
2717 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_miss_cnt
,
2718 mtxg
.lck_grp_stat
.lck_grp_mtx_stat
.lck_grp_mtx_wait_cnt
, mtxg
.lck_grp_name
)
2721 @lldb_command('showallmtx')
2722 def ShowAllMtx(cmd_args
=None):
2723 """ Routine to print a summary listing of all mutexes
2726 if kern
.ptrsize
== 8:
2727 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2729 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2731 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2733 mtxgrp_queue_head
= kern
.globals.lck_grp_queue
2734 mtxgrp_ptr_type
= GetType('_lck_grp_ *')
2736 for mtxgrp_ptr
in IterateQueue(mtxgrp_queue_head
, mtxgrp_ptr_type
, "lck_grp_link"):
2737 print GetMutexEntry(mtxgrp_ptr
)
2739 # EndMacro: showallmtx
2741 # Macro: showallrwlck
2742 @lldb_type_summary(['_lck_grp_ *'])
2743 def GetRWLEntry(rwlg
):
2744 """ Summarize a reader writer lock group with important information.
2746 rwlg: value - obj representing a reader writer lock group in kernel
2748 out_string - summary of the reader writer lock group
2752 if kern
.ptrsize
== 8:
2753 format_string
= '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2755 format_string
= '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2757 if rwlg
.lck_grp_rwcnt
:
2758 out_string
+= format_string
.format(rwlg
, rwlg
.lck_grp_rwcnt
,rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_util_cnt
,
2759 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_miss_cnt
,
2760 rwlg
.lck_grp_stat
.lck_grp_rw_stat
.lck_grp_rw_wait_cnt
, rwlg
.lck_grp_name
)
2764 @lldb_type_summary(['lck_mtx_t *'])
2765 @header("===== Mutex Lock Summary =====")
2766 def GetMutexLockSummary(mtx
):
2767 """ Summarize mutex lock with important information.
2769 mtx: value - obj representing a mutex lock in kernel
2771 out_str - summary of the mutex lock
2774 return "Invalid lock value: 0x0"
2776 if kern
.arch
== "x86_64":
2777 out_str
= "Lock Type : MUTEX\n"
2778 if mtx
.lck_mtx_tag
== 0x07ff1007 :
2779 out_str
+= "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx
.lck_mtx_ptr
)
2780 mtx
= Cast(mtx
.lck_mtx_ptr
, 'lck_mtx_t *')
2782 if mtx
.lck_mtx_tag
== 0x07fe2007 :
2783 out_str
+= "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx
.lck_mtx_tag
)
2785 out_str
+= "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx
=mtx
)
2786 out_str
+= "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx
=mtx
)
2787 out_str
+= "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx
=mtx
)
2788 out_str
+= "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx
=mtx
)
2789 out_str
+= "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx
=mtx
)
2790 out_str
+= "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx
=mtx
)
2791 out_str
+= "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx
=mtx
)
2792 out_str
+= "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx
=mtx
)
2793 if mtx
.lck_mtx_pad32
== 0xFFFFFFFF :
2794 out_str
+= "Canary (valid) : {mtx.lck_mtx_pad32:#x}\n".format(mtx
=mtx
)
2796 out_str
+= "Canary (INVALID) : {mtx.lck_mtx_pad32:#x}\n".format(mtx
=mtx
)
2799 out_str
= "Lock Type\t\t: MUTEX\n"
2800 out_str
+= "Owner Thread\t\t: {:#x}".format(mtx
.lck_mtx_data
& ~
0x3)
2801 if (mtx
.lck_mtx_data
& ~
0x3) == 0xfffffff0:
2802 out_str
+= " Held as spinlock"
2803 out_str
+= "\nNumber of Waiters\t: {:d}\n".format(mtx
.lck_mtx_waiters
)
2804 out_str
+= "Flags\t\t\t: "
2805 if mtx
.lck_mtx_data
& 0x1:
2806 out_str
+= "[Interlock Locked] "
2807 if mtx
.lck_mtx_data
& 0x2:
2808 out_str
+= "[Wait Flag]"
2811 @lldb_type_summary(['lck_spin_t *'])
2812 @header("===== SpinLock Summary =====")
2813 def GetSpinLockSummary(spinlock
):
2814 """ Summarize spinlock with important information.
2816 spinlock: value - obj representing a spinlock in kernel
2818 out_str - summary of the spinlock
2821 return "Invalid lock value: 0x0"
2823 out_str
= "Lock Type\t\t: SPINLOCK\n"
2824 if kern
.arch
== "x86_64":
2825 out_str
+= "Interlock\t\t: {:#x}\n".format(spinlock
.interlock
)
2828 lock_data
= spinlock
.hwlock
.lock_data
2830 out_str
+= "Invalid state: interlock is locked but no owner\n"
2832 out_str
+= "Owner Thread\t\t: "
2836 out_str
+= "{:#x}\n".format(lock_data
& ~
0x1)
2837 if (lock_data
& 1) == 0:
2838 out_str
+= "Invalid state: owned but interlock bit is not set\n"
2841 @lldb_command('showlock', 'MS')
2842 def ShowLock(cmd_args
=None, cmd_options
={}):
2843 """ Show info about a lock - its state and owner thread details
2844 Usage: showlock <address of a lock>
2845 -M : to consider <addr> as lck_mtx_t
2846 -S : to consider <addr> as lck_spin_t
2849 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
2854 # from osfmk/arm/locks.h
2855 LCK_SPIN_TYPE
= 0x11
2857 if kern
.arch
== "x86_64":
2858 if "-M" in cmd_options
:
2859 lock_mtx
= kern
.GetValueFromAddress(addr
, 'lck_mtx_t *')
2860 summary_str
= GetMutexLockSummary(lock_mtx
)
2861 elif "-S" in cmd_options
:
2862 lock_spin
= kern
.GetValueFromAddress(addr
, 'lck_spin_t *')
2863 summary_str
= GetSpinLockSummary(lock_spin
)
2865 summary_str
= "Please specify supported lock option(-M/-S)"
2869 lock
= kern
.GetValueFromAddress(addr
, 'uintptr_t *')
2871 lock_mtx
= Cast(lock
, 'lck_mtx_t*')
2872 if lock_mtx
.lck_mtx_type
== LCK_MTX_TYPE
:
2873 summary_str
= GetMutexLockSummary(lock_mtx
)
2875 lock_spin
= Cast(lock
, 'lck_spin_t*')
2876 if lock_spin
.type == LCK_SPIN_TYPE
:
2877 summary_str
= GetSpinLockSummary(lock_spin
)
2878 if summary_str
== "":
2879 summary_str
= "Lock Type\t\t: INVALID LOCK"
2884 @lldb_command('showallrwlck')
2885 def ShowAllRWLck(cmd_args
=None):
2886 """ Routine to print a summary listing of all read/writer locks
2888 if kern
.ptrsize
== 8:
2889 hdr_format
= '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2891 hdr_format
= '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2893 print hdr_format
.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2895 rwlgrp_queue_head
= kern
.globals.lck_grp_queue
2896 rwlgrp_ptr_type
= GetType('_lck_grp_ *')
2897 for rwlgrp_ptr
in IterateQueue(rwlgrp_queue_head
, rwlgrp_ptr_type
, "lck_grp_link"):
2898 print GetRWLEntry(rwlgrp_ptr
)
2900 # EndMacro: showallrwlck
2902 #Macro: showbootermemorymap
2903 @lldb_command('showbootermemorymap')
2904 def ShowBooterMemoryMap(cmd_args
=None):
2905 """ Prints out the phys memory map from kernelBootArgs
2906 Supported only on x86_64
2908 if kern
.arch
!= 'x86_64':
2909 print "showbootermemorymap not supported on this architecture"
2932 boot_args
= kern
.globals.kernelBootArgs
2933 msize
= boot_args
.MemoryMapDescriptorSize
2934 mcount
= (boot_args
.MemoryMapSize
) / unsigned(msize
)
2936 out_string
+= "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2940 mptr
= kern
.GetValueFromAddress(unsigned(boot_args
.MemoryMap
) + kern
.VM_MIN_KERNEL_ADDRESS
+ unsigned(i
*msize
), 'EfiMemoryRange *')
2941 mtype
= unsigned(mptr
.Type
)
2942 if mtype
in memtype_dict
:
2943 out_string
+= "{0: <12s}".format(memtype_dict
[mtype
])
2945 out_string
+= "{0: <12s}".format("UNKNOWN")
2947 if mptr
.VirtualStart
== 0:
2948 out_string
+= "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, ' '*19, mptr
.Attribute
)
2950 out_string
+= "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr
.PhysicalStart
, mptr
.NumberOfPages
, mptr
.VirtualStart
, mptr
.Attribute
)
2954 #EndMacro: showbootermemorymap
2956 @lldb_command('show_all_purgeable_objects')
2957 def ShowAllPurgeableVmObjects(cmd_args
=None):
2958 """ Routine to print a summary listing of all the purgeable vm objects
2960 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2961 ShowAllPurgeableVolatileVmObjects()
2962 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2963 ShowAllPurgeableNonVolatileVmObjects()
2965 @lldb_command('show_all_purgeable_nonvolatile_objects')
2966 def ShowAllPurgeableNonVolatileVmObjects(cmd_args
=None):
2967 """ Routine to print a summary listing of all the vm objects in
2968 the purgeable_nonvolatile_queue
2971 nonvolatile_total
= lambda:None
2972 nonvolatile_total
.objects
= 0
2973 nonvolatile_total
.vsize
= 0
2974 nonvolatile_total
.rsize
= 0
2975 nonvolatile_total
.wsize
= 0
2976 nonvolatile_total
.csize
= 0
2977 nonvolatile_total
.disowned_objects
= 0
2978 nonvolatile_total
.disowned_vsize
= 0
2979 nonvolatile_total
.disowned_rsize
= 0
2980 nonvolatile_total
.disowned_wsize
= 0
2981 nonvolatile_total
.disowned_csize
= 0
2983 queue_len
= kern
.globals.purgeable_nonvolatile_count
2984 queue_head
= kern
.globals.purgeable_nonvolatile_queue
2986 print 'purgeable_nonvolatile_queue:{: <#018x} purgeable_volatile_count:{:d}\n'.format(kern
.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len
)
2987 print 'N:non-volatile V:volatile E:empty D:deny\n'
2989 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
2991 for object in IterateQueue(queue_head
, 'struct vm_object *', 'objq'):
2993 ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
)
2994 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
)
2995 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
)
2998 def ShowPurgeableNonVolatileVmObject(object, idx
, queue_len
, nonvolatile_total
):
2999 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
3001 object - core.value : a object of type 'struct vm_object *'
3005 page_size
= kern
.globals.page_size
3006 if object.purgable
== 0:
3008 elif object.purgable
== 1:
3010 elif object.purgable
== 2:
3012 elif object.purgable
== 3:
3016 if object.pager
== 0:
3017 compressed_count
= 0
3019 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
3020 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
3022 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx
,queue_len
,object,purgable
,object.ref_count
,object.vo_un1
.vou_size
/page_size
,object.resident_page_count
,object.wired_page_count
,compressed_count
, object.vo_ledger_tag
, object.vo_un2
.vou_owner
,GetProcPIDForObjectOwner(object.vo_un2
.vou_owner
),GetProcNameForObjectOwner(object.vo_un2
.vou_owner
))
3024 nonvolatile_total
.objects
+= 1
3025 nonvolatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
3026 nonvolatile_total
.rsize
+= object.resident_page_count
3027 nonvolatile_total
.wsize
+= object.wired_page_count
3028 nonvolatile_total
.csize
+= compressed_count
3029 if object.vo_un2
.vou_owner
== 0:
3030 nonvolatile_total
.disowned_objects
+= 1
3031 nonvolatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
3032 nonvolatile_total
.disowned_rsize
+= object.resident_page_count
3033 nonvolatile_total
.disowned_wsize
+= object.wired_page_count
3034 nonvolatile_total
.disowned_csize
+= compressed_count
3037 @lldb_command('show_all_purgeable_volatile_objects')
3038 def ShowAllPurgeableVolatileVmObjects(cmd_args
=None):
3039 """ Routine to print a summary listing of all the vm objects in
3040 the purgeable queues
3042 volatile_total
= lambda:None
3043 volatile_total
.objects
= 0
3044 volatile_total
.vsize
= 0
3045 volatile_total
.rsize
= 0
3046 volatile_total
.wsize
= 0
3047 volatile_total
.csize
= 0
3048 volatile_total
.disowned_objects
= 0
3049 volatile_total
.disowned_vsize
= 0
3050 volatile_total
.disowned_rsize
= 0
3051 volatile_total
.disowned_wsize
= 0
3052 volatile_total
.disowned_csize
= 0
3054 purgeable_queues
= kern
.globals.purgeable_queues
3055 print "---------- OBSOLETE\n"
3056 ShowPurgeableQueue(purgeable_queues
[0], volatile_total
)
3057 print "\n\n---------- FIFO\n"
3058 ShowPurgeableQueue(purgeable_queues
[1], volatile_total
)
3059 print "\n\n---------- LIFO\n"
3060 ShowPurgeableQueue(purgeable_queues
[2], volatile_total
)
3062 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
)
3063 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
)
3064 purgeable_count
= kern
.globals.vm_page_purgeable_count
3065 purgeable_wired_count
= kern
.globals.vm_page_purgeable_wired_count
3066 if purgeable_count
!= volatile_total
.rsize
or purgeable_wired_count
!= volatile_total
.wsize
:
3067 mismatch
= "<--------- MISMATCH\n"
3070 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count
, purgeable_wired_count
, mismatch
)
3073 def ShowPurgeableQueue(qhead
, volatile_total
):
3074 print "----- GROUP 0\n"
3075 ShowPurgeableGroup(qhead
.objq
[0], volatile_total
)
3076 print "----- GROUP 1\n"
3077 ShowPurgeableGroup(qhead
.objq
[1], volatile_total
)
3078 print "----- GROUP 2\n"
3079 ShowPurgeableGroup(qhead
.objq
[2], volatile_total
)
3080 print "----- GROUP 3\n"
3081 ShowPurgeableGroup(qhead
.objq
[3], volatile_total
)
3082 print "----- GROUP 4\n"
3083 ShowPurgeableGroup(qhead
.objq
[4], volatile_total
)
3084 print "----- GROUP 5\n"
3085 ShowPurgeableGroup(qhead
.objq
[5], volatile_total
)
3086 print "----- GROUP 6\n"
3087 ShowPurgeableGroup(qhead
.objq
[6], volatile_total
)
3088 print "----- GROUP 7\n"
3089 ShowPurgeableGroup(qhead
.objq
[7], volatile_total
)
3091 def ShowPurgeableGroup(qhead
, volatile_total
):
3093 for object in IterateQueue(qhead
, 'struct vm_object *', 'objq'):
3095 # 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","")
3096 print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
3098 ShowPurgeableVolatileVmObject(object, idx
, volatile_total
)
3100 def ShowPurgeableVolatileVmObject(object, idx
, volatile_total
):
3101 """ Routine to print out a summary a VM object in a purgeable queue
3103 object - core.value : a object of type 'struct vm_object *'
3107 ## if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
3111 page_size
= kern
.globals.page_size
3112 if object.purgable
== 0:
3114 elif object.purgable
== 1:
3116 elif object.purgable
== 2:
3118 elif object.purgable
== 3:
3122 if object.pager
== 0:
3123 compressed_count
= 0
3125 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
3126 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
3127 # print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {: <#018x} {:>6d} {:<20s} {: <#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner),object.vo_purgeable_volatilizer,GetProcPIDForObjectOwner(object.vo_purgeable_volatilizer),GetProcNameForObjectOwner(object.vo_purgeable_volatilizer),diff)
3128 print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx
,object,purgable
,object.ref_count
,object.vo_un1
.vou_size
/page_size
,object.resident_page_count
,object.wired_page_count
,compressed_count
, object.vo_ledger_tag
, object.vo_un2
.vou_owner
,GetProcPIDForObjectOwner(object.vo_un2
.vou_owner
),GetProcNameForObjectOwner(object.vo_un2
.vou_owner
))
3129 volatile_total
.objects
+= 1
3130 volatile_total
.vsize
+= object.vo_un1
.vou_size
/page_size
3131 volatile_total
.rsize
+= object.resident_page_count
3132 volatile_total
.wsize
+= object.wired_page_count
3133 volatile_total
.csize
+= compressed_count
3134 if object.vo_un2
.vou_owner
== 0:
3135 volatile_total
.disowned_objects
+= 1
3136 volatile_total
.disowned_vsize
+= object.vo_un1
.vou_size
/page_size
3137 volatile_total
.disowned_rsize
+= object.resident_page_count
3138 volatile_total
.disowned_wsize
+= object.wired_page_count
3139 volatile_total
.disowned_csize
+= compressed_count
3142 def GetCompressedPagesForObject(obj
):
3145 pager
= Cast(obj
.pager
, 'compressor_pager_t')
3146 return pager
.cpgr_num_slots_occupied
3147 """ # commented code below
3148 if pager.cpgr_num_slots > 128:
3149 slots_arr = pager.cpgr_slots.cpgr_islots
3150 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
3153 compressed_pages = 0
3154 while index < num_indirect_slot_ptr:
3156 if slots_arr[index]:
3157 while compressor_slot < 128:
3158 if slots_arr[index][compressor_slot]:
3159 compressed_pages += 1
3160 compressor_slot += 1
3163 slots_arr = pager.cpgr_slots.cpgr_dslots
3165 compressed_pages = 0
3166 while compressor_slot < pager.cpgr_num_slots:
3167 if slots_arr[compressor_slot]:
3168 compressed_pages += 1
3169 compressor_slot += 1
3170 return compressed_pages
3173 def ShowTaskVMEntries(task
, show_pager_info
, show_all_shadows
):
3174 """ Routine to print out a summary listing of all the entries in a vm_map
3176 task - core.value : a object of type 'task *'
3180 print "vm_map entries for task " + hex(task
)
3181 print GetTaskSummary
.header
3182 print GetTaskSummary(task
)
3184 print "Task {0: <#020x} has map = 0x0"
3186 showmapvme(task
.map, 0, 0, show_pager_info
, show_all_shadows
, False)
3188 @lldb_command("showmapvme", "A:B:F:PRST")
3189 def ShowMapVME(cmd_args
=None, cmd_options
={}):
3190 """Routine to print out info about the specified vm_map and its vm entries
3191 usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
3192 Use -A <start> flag to start at virtual address <start>
3193 Use -B <end> flag to end at virtual address <end>
3194 Use -F <virtaddr> flag to find just the VME containing the given VA
3195 Use -S flag to show VM object shadow chains
3196 Use -P flag to show pager info (mapped file, compressed pages, ...)
3197 Use -R flag to reverse order
3198 Use -T to show red-black tree pointers
3200 if cmd_args
== None or len(cmd_args
) < 1:
3201 print "Invalid argument.", ShowMapVME
.__doc
__
3203 show_pager_info
= False
3204 show_all_shadows
= False
3205 show_rb_tree
= False
3208 reverse_order
= False
3209 if "-A" in cmd_options
:
3210 start_vaddr
= unsigned(int(cmd_options
['-A'], 16))
3211 if "-B" in cmd_options
:
3212 end_vaddr
= unsigned(int(cmd_options
['-B'], 16))
3213 if "-F" in cmd_options
:
3214 start_vaddr
= unsigned(int(cmd_options
['-F'], 16))
3215 end_vaddr
= start_vaddr
3216 if "-P" in cmd_options
:
3217 show_pager_info
= True
3218 if "-S" in cmd_options
:
3219 show_all_shadows
= True
3220 if "-R" in cmd_options
:
3221 reverse_order
= True
3222 if "-T" in cmd_options
:
3224 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
3225 showmapvme(map, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
3227 @lldb_command("showmapcopyvme", "A:B:F:PRST")
3228 def ShowMapCopyVME(cmd_args
=None, cmd_options
={}):
3229 """Routine to print out info about the specified vm_map_copy and its vm entries
3230 usage: showmapcopyvme <vm_map_copy> [-A start] [-B end] [-S] [-P]
3231 Use -A <start> flag to start at virtual address <start>
3232 Use -B <end> flag to end at virtual address <end>
3233 Use -F <virtaddr> flag to find just the VME containing the given VA
3234 Use -S flag to show VM object shadow chains
3235 Use -P flag to show pager info (mapped file, compressed pages, ...)
3236 Use -R flag to reverse order
3237 Use -T to show red-black tree pointers
3239 if cmd_args
== None or len(cmd_args
) < 1:
3240 print "Invalid argument.", ShowMapVME
.__doc
__
3242 show_pager_info
= False
3243 show_all_shadows
= False
3244 show_rb_tree
= False
3247 reverse_order
= False
3248 if "-A" in cmd_options
:
3249 start_vaddr
= unsigned(int(cmd_options
['-A'], 16))
3250 if "-B" in cmd_options
:
3251 end_vaddr
= unsigned(int(cmd_options
['-B'], 16))
3252 if "-F" in cmd_options
:
3253 start_vaddr
= unsigned(int(cmd_options
['-F'], 16))
3254 end_vaddr
= start_vaddr
3255 if "-P" in cmd_options
:
3256 show_pager_info
= True
3257 if "-S" in cmd_options
:
3258 show_all_shadows
= True
3259 if "-R" in cmd_options
:
3260 reverse_order
= True
3261 if "-T" in cmd_options
:
3263 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_copy_t')
3264 showmapcopyvme(map, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
3266 @lldb_command("showvmobject", "A:B:PRST")
3267 def ShowVMObject(cmd_args
=None, cmd_options
={}):
3268 """Routine to print out a VM object and its shadow chain
3269 usage: showvmobject <vm_object> [-S] [-P]
3270 -S: show VM object shadow chain
3271 -P: show pager info (mapped file, compressed pages, ...)
3273 if cmd_args
== None or len(cmd_args
) < 1:
3274 print "Invalid argument.", ShowMapVME
.__doc
__
3276 show_pager_info
= False
3277 show_all_shadows
= False
3278 if "-P" in cmd_options
:
3279 show_pager_info
= True
3280 if "-S" in cmd_options
:
3281 show_all_shadows
= True
3282 object = kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
3283 showvmobject(object, 0, 0, show_pager_info
, show_all_shadows
)
3285 def showvmobject(object, offset
=0, size
=0, show_pager_info
=False, show_all_shadows
=False):
3286 page_size
= kern
.globals.page_size
3287 vnode_pager_ops
= kern
.globals.vnode_pager_ops
3288 vnode_pager_ops_addr
= unsigned(addressof(vnode_pager_ops
))
3290 if size
== 0 and object != 0 and object.internal
:
3291 size
= object.vo_un1
.vou_size
3294 if show_all_shadows
== False and depth
!= 1 and object.shadow
!= 0:
3295 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
3296 object = object.shadow
3298 if object.copy_strategy
== 0:
3300 elif object.copy_strategy
== 2:
3302 elif object.copy_strategy
== 4:
3306 copy_strategy
=str(object.copy_strategy
)
3308 internal
= "internal"
3310 internal
= "external"
3311 purgeable
= "NVED"[int(object.purgable
)]
3313 if object.phys_contiguous
:
3314 pager_string
= pager_string
+ "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2
.vou_shadow_offset
), unsigned(object.vo_un1
.vou_size
))
3315 pager
= object.pager
3316 if show_pager_info
and pager
!= 0:
3318 pager_string
= pager_string
+ "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
3319 elif unsigned(pager
.mo_pager_ops
) == vnode_pager_ops_addr
:
3320 vnode_pager
= Cast(pager
,'vnode_pager *')
3321 pager_string
= pager_string
+ "-> " + GetVnodePath(vnode_pager
.vnode_handle
)
3323 pager_string
= pager_string
+ "-> {:s}:{: <#018x}".format(pager
.mo_pager_ops
.memory_object_pager_name
, pager
)
3324 print "{:>18d} {:#018x}:{:#018x} {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} purg:{:1s} {:s} wtag:{:d} ({:d} {:d} {:d}) {:s}".format(depth
,offset
,offset
+size
,object,object.ref_count
,object.true_share
,copy_strategy
,purgeable
,internal
,object.wire_tag
,unsigned(object.vo_un1
.vou_size
)/page_size
,object.resident_page_count
,object.wired_page_count
,pager_string
)
3325 # 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)
3326 offset
+= unsigned(object.vo_un2
.vou_shadow_offset
)
3327 object = object.shadow
3329 def showmapvme(map, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
=False, show_rb_tree
=False):
3332 rsize
= int(map.pmap
.stats
.resident_count
)
3333 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map","pmap","size","#ents","rsize","start","end","pgshift")
3334 print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(map,map.pmap
,unsigned(map.size
),map.hdr
.nentries
,rsize
,map.hdr
.links
.start
,map.hdr
.links
.end
,map.hdr
.page_shift
)
3335 showmaphdrvme(map.hdr
, map.pmap
, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
3337 def showmapcopyvme(mapcopy
, start_vaddr
=0, end_vaddr
=0, show_pager_info
=True, show_all_shadows
=True, reverse_order
=False, show_rb_tree
=False):
3338 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s} {:<7s}".format("vm_map_copy","offset","size","#ents","rsize","start","end","pgshift")
3339 print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x} {:>7d}".format(mapcopy
,mapcopy
.offset
,mapcopy
.size
,mapcopy
.c_u
.hdr
.nentries
,0,mapcopy
.c_u
.hdr
.links
.start
,mapcopy
.c_u
.hdr
.links
.end
,mapcopy
.c_u
.hdr
.page_shift
)
3340 showmaphdrvme(mapcopy
.c_u
.hdr
, 0, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
)
3342 def showmaphdrvme(maphdr
, pmap
, start_vaddr
, end_vaddr
, show_pager_info
, show_all_shadows
, reverse_order
, show_rb_tree
):
3343 page_size
= kern
.globals.page_size
3344 vnode_pager_ops
= kern
.globals.vnode_pager_ops
3345 vnode_pager_ops_addr
= unsigned(addressof(vnode_pager_ops
))
3346 if hasattr(kern
.globals, 'compressor_object'):
3347 compressor_object
= kern
.globals.compressor_object
3349 compressor_object
= -1;
3350 vme_list_head
= maphdr
.links
3351 vme_ptr_type
= GetType('vm_map_entry *')
3352 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
3353 last_end
= unsigned(maphdr
.links
.start
)
3355 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links", reverse_order
):
3356 if start_vaddr
!= 0 and end_vaddr
!= 0:
3357 if unsigned(vme
.links
.start
) > end_vaddr
:
3359 if unsigned(vme
.links
.end
) <= start_vaddr
:
3360 last_end
= unsigned(vme
.links
.end
)
3361 skipped_entries
= skipped_entries
+ 1
3363 if skipped_entries
!= 0:
3364 print "... skipped {:d} entries ...".format(skipped_entries
)
3366 if unsigned(vme
.links
.start
) != last_end
:
3367 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,vme
.links
.start
,(unsigned(vme
.links
.start
) - last_end
)/page_size
)
3368 last_end
= unsigned(vme
.links
.end
)
3369 size
= unsigned(vme
.links
.end
) - unsigned(vme
.links
.start
)
3370 object = vme
.vme_object
.vmo_object
3372 object_str
= "{: <#018x}".format(object)
3373 elif vme
.is_sub_map
:
3374 if object == kern
.globals.bufferhdr_map
:
3375 object_str
= "BUFFERHDR_MAP"
3376 elif object == kern
.globals.mb_map
:
3377 object_str
= "MB_MAP"
3378 elif object == kern
.globals.bsd_pageable_map
:
3379 object_str
= "BSD_PAGEABLE_MAP"
3380 elif object == kern
.globals.ipc_kernel_map
:
3381 object_str
= "IPC_KERNEL_MAP"
3382 elif object == kern
.globals.ipc_kernel_copy_map
:
3383 object_str
= "IPC_KERNEL_COPY_MAP"
3384 elif object == kern
.globals.kalloc_map
:
3385 object_str
= "KALLOC_MAP"
3386 elif hasattr(kern
.globals, 'compressor_map') and object == kern
.globals.compressor_map
:
3387 object_str
= "COMPRESSOR_MAP"
3388 elif hasattr(kern
.globals, 'gzalloc_map') and object == kern
.globals.gzalloc_map
:
3389 object_str
= "GZALLOC_MAP"
3390 elif hasattr(kern
.globals, 'g_kext_map') and object == kern
.globals.g_kext_map
:
3391 object_str
= "G_KEXT_MAP"
3392 elif hasattr(kern
.globals, 'vector_upl_submap') and object == kern
.globals.vector_upl_submap
:
3393 object_str
= "VECTOR_UPL_SUBMAP"
3395 object_str
= "submap:{: <#018x}".format(object)
3397 if object == kern
.globals.kernel_object
:
3398 object_str
= "KERNEL_OBJECT"
3399 elif object == kern
.globals.vm_submap_object
:
3400 object_str
= "VM_SUBMAP_OBJECT"
3401 elif object == compressor_object
:
3402 object_str
= "COMPRESSOR_OBJECT"
3404 object_str
= "{: <#018x}".format(object)
3405 offset
= unsigned(vme
.vme_offset
) & ~
0xFFF
3406 tag
= unsigned(vme
.vme_offset
& 0xFFF)
3408 if vme
.protection
& 0x1:
3412 if vme
.protection
& 0x2:
3416 if vme
.protection
& 0x4:
3421 if vme
.max_protection
& 0x1:
3422 max_protection
+="r"
3424 max_protection
+= "-"
3425 if vme
.max_protection
& 0x2:
3426 max_protection
+= "w"
3428 max_protection
+= "-"
3429 if vme
.max_protection
& 0x4:
3430 max_protection
+= "x"
3432 max_protection
+= "-"
3442 if vme
.used_for_jit
:
3445 if pmap
== kern
.globals.kernel_pmap
:
3446 xsite
= Cast(kern
.globals.vm_allocation_sites
[tag
],'OSKextAccount *')
3447 if xsite
and xsite
.site
.flags
& 0x0200:
3448 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
3451 rb_info
= "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme
.store
.entry
.rbe_left
, vme
.store
.entry
.rbe_right
, vme
.store
.entry
.rbe_parent
)
3452 print "{: <#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:3s}/{:3s}/{:<8s} {:<18s} {:<#18x} {:s}".format(vme
,vme
.links
.start
,vme
.links
.end
,(unsigned(vme
.links
.end
)-unsigned(vme
.links
.start
))/page_size
,tag
,tagstr
,protection
,max_protection
,vme_flags
,object_str
,offset
, rb_info
)
3453 if (show_pager_info
or show_all_shadows
) and vme
.is_sub_map
== 0 and vme
.vme_object
.vmo_object
!= 0:
3454 object = vme
.vme_object
.vmo_object
3457 showvmobject(object, offset
, size
, show_pager_info
, show_all_shadows
)
3458 if start_vaddr
!= 0 or end_vaddr
!= 0:
3460 elif unsigned(maphdr
.links
.end
) > last_end
:
3461 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end
,maphdr
.links
.end
,(unsigned(maphdr
.links
.end
) - last_end
)/page_size
)
3464 def CountMapTags(map, tagcounts
, slow
):
3465 page_size
= unsigned(kern
.globals.page_size
)
3466 vme_list_head
= map.hdr
.links
3467 vme_ptr_type
= GetType('vm_map_entry *')
3468 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
3469 object = vme
.vme_object
.vmo_object
3470 tag
= vme
.vme_offset
& 0xFFF
3471 if object == kern
.globals.kernel_object
:
3474 count
= unsigned(vme
.links
.end
- vme
.links
.start
) / page_size
3476 addr
= unsigned(vme
.links
.start
)
3477 while addr
< unsigned(vme
.links
.end
):
3478 hash_id
= _calc_vm_page_hash(object, addr
)
3479 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3480 page
= _vm_page_unpack_ptr(page_list
)
3482 vmpage
= kern
.GetValueFromAddress(page
, 'vm_page_t')
3483 if (addr
== unsigned(vmpage
.vmp_offset
)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage
.vmp_object
))):
3484 if (not vmpage
.vmp_local
) and (vmpage
.vmp_wire_count
> 0):
3487 page
= _vm_page_unpack_ptr(vmpage
.vmp_next_m
)
3489 tagcounts
[tag
] += count
3490 elif vme
.is_sub_map
:
3491 CountMapTags(Cast(object,'vm_map_t'), tagcounts
, slow
)
3494 def CountWiredObject(object, tagcounts
):
3495 tagcounts
[unsigned(object.wire_tag
)] += object.wired_page_count
3498 def GetKmodIDName(kmod_id
):
3499 kmod_val
= kern
.globals.kmod
3500 for kmod
in IterateLinkedList(kmod_val
, 'next'):
3501 if (kmod
.id == kmod_id
):
3502 return "{:<50s}".format(kmod
.name
)
3506 0: "VM_KERN_MEMORY_NONE",
3507 1: "VM_KERN_MEMORY_OSFMK",
3508 2: "VM_KERN_MEMORY_BSD",
3509 3: "VM_KERN_MEMORY_IOKIT",
3510 4: "VM_KERN_MEMORY_LIBKERN",
3511 5: "VM_KERN_MEMORY_OSKEXT",
3512 6: "VM_KERN_MEMORY_KEXT",
3513 7: "VM_KERN_MEMORY_IPC",
3514 8: "VM_KERN_MEMORY_STACK",
3515 9: "VM_KERN_MEMORY_CPU",
3516 10: "VM_KERN_MEMORY_PMAP",
3517 11: "VM_KERN_MEMORY_PTE",
3518 12: "VM_KERN_MEMORY_ZONE",
3519 13: "VM_KERN_MEMORY_KALLOC",
3520 14: "VM_KERN_MEMORY_COMPRESSOR",
3521 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
3522 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
3523 17: "VM_KERN_MEMORY_WAITQ",
3524 18: "VM_KERN_MEMORY_DIAG",
3525 19: "VM_KERN_MEMORY_LOG",
3526 20: "VM_KERN_MEMORY_FILE",
3527 21: "VM_KERN_MEMORY_MBUF",
3528 22: "VM_KERN_MEMORY_UBC",
3529 23: "VM_KERN_MEMORY_SECURITY",
3530 24: "VM_KERN_MEMORY_MLOCK",
3531 25: "VM_KERN_MEMORY_REASON",
3532 26: "VM_KERN_MEMORY_SKYWALK",
3533 27: "VM_KERN_MEMORY_LTABLE",
3534 28: "VM_KERN_MEMORY_HV",
3535 29: "VM_KERN_MEMORY_RETIRED",
3536 255:"VM_KERN_MEMORY_ANY",
3539 def GetVMKernName(tag
):
3540 """ returns the formatted name for a vmtag and
3541 the sub-tag for kmod tags.
3543 if ((tag
<= 27) or (tag
== 255)):
3544 return (FixedTags
[tag
], "")
3545 site
= kern
.globals.vm_allocation_sites
[tag
]
3547 if site
.flags
& 0x007F:
3548 cstr
= addressof(site
.subtotals
[site
.subtotalscount
])
3549 return ("{:<50s}".format(str(Cast(cstr
, 'char *'))), "")
3551 if site
.flags
& 0x0200:
3552 xsite
= Cast(site
,'OSKextAccount *')
3553 tagstr
= ".{:<3d}".format(xsite
.loadTag
)
3554 return (GetKmodIDName(xsite
.loadTag
), tagstr
);
3556 return (kern
.Symbolicate(site
), "")
3559 @lldb_command("showvmtags", "ASJO")
3560 def showvmtags(cmd_args
=None, cmd_options
={}):
3561 """Routine to print out info about kernel wired page allocations
3563 iterates kernel map and vm objects totaling allocations by tag.
3564 usage: showvmtags -S [-O]
3565 also iterates kernel object pages individually - slow.
3566 usage: showvmtags -A [-O]
3567 show all tags, even tags that have no wired count
3568 usage: showvmtags -J [-O]
3571 -O: list in increasing size order
3575 if "-S" in cmd_options
:
3578 if "-A" in cmd_options
:
3580 if "-J" in cmd_options
:
3583 page_size
= unsigned(kern
.globals.page_size
)
3584 nsites
= unsigned(kern
.globals.vm_allocation_tag_highest
) + 1
3585 tagcounts
= [0] * nsites
3586 tagmapped
= [0] * nsites
3588 if kern
.globals.vm_tag_active_update
:
3589 for tag
in range(nsites
):
3590 site
= kern
.globals.vm_allocation_sites
[tag
]
3592 tagcounts
[tag
] = unsigned(site
.total
)
3593 tagmapped
[tag
] = unsigned(site
.mapped
)
3595 queue_head
= kern
.globals.vm_objects_wired
3596 for object in IterateQueue(queue_head
, 'struct vm_object *', 'wired_objq'):
3597 if object != kern
.globals.kernel_object
:
3598 CountWiredObject(object, tagcounts
)
3600 CountMapTags(kern
.globals.kernel_map
, tagcounts
, slow
)
3605 for tag
in range(nsites
):
3606 if all_tags
or tagcounts
[tag
] or tagmapped
[tag
]:
3608 total
+= tagcounts
[tag
]
3609 totalmapped
+= tagmapped
[tag
]
3610 (sitestr
, tagstr
) = GetVMKernName(tag
)
3611 current
["name"] = sitestr
3612 current
["size"] = tagcounts
[tag
]
3613 current
["mapped"] = tagmapped
[tag
]
3614 current
["tag"] = tag
3615 current
["tagstr"] = tagstr
3616 current
["subtotals"] = []
3618 site
= kern
.globals.vm_allocation_sites
[tag
]
3619 for sub
in range(site
.subtotalscount
):
3620 alloctag
= unsigned(site
.subtotals
[sub
].tag
)
3621 amount
= unsigned(site
.subtotals
[sub
].total
)
3622 subsite
= kern
.globals.vm_allocation_sites
[alloctag
]
3623 if alloctag
and subsite
:
3624 (sitestr
, tagstr
) = GetVMKernName(alloctag
)
3625 current
["subtotals"].append({
3627 "flags": int(subsite
.flags
),
3632 tags
.append(current
)
3634 if "-O" in cmd_options
:
3635 tags
.sort(key
= lambda tag
: tag
['size'])
3638 print json
.dumps(tags
)
3640 print " vm_allocation_tag_highest: {:<7d} ".format(nsites
- 1)
3641 print " {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "size", "mapped", "name")
3645 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag
["tag"], tag
["tagstr"], tag
["size"] / 1024, tag
["mapped"] / 1024, tag
["name"])
3646 for sub
in tag
["subtotals"]:
3647 if ((sub
["flags"] & 0x007f) == 0):
3652 print " {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", sub
["amount"] / 1024, kind_str
, sub
["tag"], sub
["tagstr"], sub
["sitestr"])
3654 print "Total: {:>7d}K {:>7d}K".format(total
/ 1024, totalmapped
/ 1024)
3658 def FindVMEntriesForVnode(task
, vn
):
3659 """ returns an array of vme that have the vnode set to defined vnode
3660 each entry in array is of format (vme, start_addr, end_address, protection)
3665 pager_ops_addr
= unsigned(addressof(kern
.globals.vnode_pager_ops
))
3666 debuglog("pager_ops_addr %s" % hex(pager_ops_addr
))
3668 if unsigned(pmap
) == 0:
3670 vme_list_head
= vmmap
.hdr
.links
3671 vme_ptr_type
= gettype('vm_map_entry *')
3672 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, 'links'):
3674 if unsigned(vme
.is_sub_map
) == 0 and unsigned(vme
.vme_object
.vmo_object
) != 0:
3675 obj
= vme
.vme_object
.vmo_object
3684 vn_pager
= Cast(obj
.pager
, 'vnode_pager *')
3685 if unsigned(vn_pager
.vn_pgr_hdr
.mo_pager_ops
) == pager_ops_addr
and unsigned(vn_pager
.vnode_handle
) == unsigned(vn
):
3686 retval
.append((vme
, unsigned(vme
.links
.start
), unsigned(vme
.links
.end
), unsigned(vme
.protection
)))
3690 @lldb_command('showtaskloadinfo')
3691 def ShowTaskLoadInfo(cmd_args
=None, cmd_options
={}):
3692 """ Print the load address and uuid for the process
3693 Usage: (lldb)showtaskloadinfo <task_t>
3696 raise ArgumentError("Insufficient arguments")
3697 t
= kern
.GetValueFromAddress(cmd_args
[0], 'struct task *')
3698 print_format
= "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
3699 p
= Cast(t
.bsd_info
, 'struct proc *')
3701 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
)
3702 filepath
= GetVnodePath(p
.p_textvp
)
3703 libname
= filepath
.split('/')[-1]
3704 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
3705 mappings
= FindVMEntriesForVnode(t
, p
.p_textvp
)
3712 #print "Load address: %s" % hex(m[1])
3713 print print_format
.format(load_addr
, end_addr
, libname
, uuid_out_string
, filepath
)
3716 @header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3717 @lldb_command('vmpagelookup')
3718 def VMPageLookup(cmd_args
=None):
3719 """ Print the pages in the page bucket corresponding to the provided object and offset.
3720 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3722 if cmd_args
== None or len(cmd_args
) < 2:
3723 raise ArgumentError("Please specify an object and offset.")
3724 format_string
= "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3726 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3727 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3729 hash_id
= _calc_vm_page_hash(obj
, off
)
3731 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
3732 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(page_list
)))
3734 print VMPageLookup
.header
3735 page
= _vm_page_unpack_ptr(page_list
)
3737 pg_t
= kern
.GetValueFromAddress(page
, 'vm_page_t')
3738 print format_string
.format(page
, pg_t
.vmp_offset
, _vm_page_unpack_ptr(pg_t
.vmp_object
))
3739 page
= _vm_page_unpack_ptr(pg_t
.vmp_next_m
)
3743 @lldb_command('vmpage_get_phys_page')
3744 def VmPageGetPhysPage(cmd_args
=None):
3745 """ return the physical page for a vm_page_t
3746 usage: vm_page_get_phys_page <vm_page_t>
3748 if cmd_args
== None or len(cmd_args
) < 1:
3749 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
3752 page
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_page_t')
3753 phys_page
= _vm_page_get_phys_page(page
)
3754 print("phys_page = 0x%x\n" % phys_page
)
3757 def _vm_page_get_phys_page(page
):
3758 if kern
.arch
== 'x86_64':
3759 return page
.vmp_phys_page
3765 if m
>= unsigned(kern
.globals.vm_page_array_beginning_addr
) and m
< unsigned(kern
.globals.vm_page_array_ending_addr
) :
3766 return (m
- unsigned(kern
.globals.vm_page_array_beginning_addr
)) / sizeof('struct vm_page') + unsigned(kern
.globals.vm_first_phys_ppnum
)
3768 page_with_ppnum
= Cast(page
, 'uint32_t *')
3769 ppnum_offset
= sizeof('struct vm_page') / sizeof('uint32_t')
3770 return page_with_ppnum
[ppnum_offset
]
3773 @lldb_command('vmpage_unpack_ptr')
3774 def VmPageUnpackPtr(cmd_args
=None):
3775 """ unpack a pointer
3776 usage: vm_page_unpack_ptr <packed_ptr>
3778 if cmd_args
== None or len(cmd_args
) < 1:
3779 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
3782 packed
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long')
3783 unpacked
= _vm_page_unpack_ptr(packed
)
3784 print("unpacked pointer = 0x%x\n" % unpacked
)
3787 def _vm_page_unpack_ptr(page
):
3788 if kern
.ptrsize
== 4 :
3794 params
= kern
.globals.vm_page_packing_params
3795 ptr_shift
= params
.vmpp_shift
3796 ptr_mask
= kern
.globals.vm_packed_from_vm_pages_array_mask
3798 # when no mask and shift on 64bit systems, we're working with real/non-packed pointers
3799 if ptr_shift
== 0 and ptr_mask
== 0:
3802 if unsigned(page
) & unsigned(ptr_mask
):
3803 masked_page
= (unsigned(page
) & ~ptr_mask
)
3804 # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
3805 vm_pages_addr
= unsigned(addressof(kern
.globals.vm_pages
[0]))
3806 element_size
= unsigned(addressof(kern
.globals.vm_pages
[1])) - vm_pages_addr
3807 return (vm_pages_addr
+ masked_page
* element_size
)
3808 return unsigned(vm_unpack_pointer(page
, params
))
3810 @lldb_command('calcvmpagehash')
3811 def CalcVMPageHash(cmd_args
=None):
3812 """ Get the page bucket corresponding to the provided object and offset.
3813 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3815 if cmd_args
== None or len(cmd_args
) < 2:
3816 raise ArgumentError("Please specify an object and offset.")
3818 obj
= kern
.GetValueFromAddress(cmd_args
[0],'unsigned long long')
3819 off
= kern
.GetValueFromAddress(cmd_args
[1],'unsigned long long')
3821 hash_id
= _calc_vm_page_hash(obj
, off
)
3823 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id
), unsigned(kern
.globals.vm_page_buckets
[hash_id
].page_list
)))
3826 def _calc_vm_page_hash(obj
, off
):
3827 bucket_hash
= (int) (kern
.globals.vm_page_bucket_hash
)
3828 hash_mask
= (int) (kern
.globals.vm_page_hash_mask
)
3830 one
= (obj
* bucket_hash
) & 0xFFFFFFFF
3831 two
= off
>> unsigned(kern
.globals.page_shift
)
3832 three
= two ^ bucket_hash
3834 hash_id
= four
& hash_mask
3838 #Macro: showallocatedzoneelement
3839 @lldb_command('showallocatedzoneelement')
3840 def ShowAllocatedElementsInZone(cmd_args
=None, cmd_options
={}):
3841 """ Show all the allocated elements in a zone
3842 usage: showzoneallocelements <address of zone>
3844 if len(cmd_args
) < 1:
3845 raise ArgumentError("Please specify a zone")
3847 zone
= kern
.GetValueFromAddress(cmd_args
[0], 'struct zone *')
3848 elements
= FindAllocatedElementsInZone(zone
)
3850 for elem
in elements
:
3851 print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i
, len(elements
), elem
)
3854 #EndMacro: showallocatedzoneelement
3856 def FindAllocatedElementsInZone(zone
):
3859 if not zone
.z_self
or zone
.z_permanent
:
3862 for head
in [zone
.z_pageq_partial
, zone
.z_pageq_full
]:
3863 for meta
in ZoneIteratePageQueue(head
):
3864 for elem
in meta
.iterateElements():
3865 if not meta
.isElementFree(elem
):
3866 elements
.append(elem
)
3870 def match_vm_page_attributes(page
, matching_attributes
):
3871 page_ptr
= addressof(page
)
3872 unpacked_vm_object
= _vm_page_unpack_ptr(page
.vmp_object
)
3873 matched_attributes
= 0
3874 if "vmp_q_state" in matching_attributes
and (page
.vmp_q_state
== matching_attributes
["vmp_q_state"]):
3875 matched_attributes
+= 1
3876 if "vm_object" in matching_attributes
and (unsigned(unpacked_vm_object
) == unsigned(matching_attributes
["vm_object"])):
3877 matched_attributes
+= 1
3878 if "vmp_offset" in matching_attributes
and (unsigned(page
.vmp_offset
) == unsigned(matching_attributes
["vmp_offset"])):
3879 matched_attributes
+= 1
3880 if "phys_page" in matching_attributes
and (unsigned(_vm_page_get_phys_page(page_ptr
)) == unsigned(matching_attributes
["phys_page"])):
3881 matched_attributes
+= 1
3882 if "bitfield" in matching_attributes
and unsigned(page
.__getattr
__(matching_attributes
["bitfield"])) == 1:
3883 matched_attributes
+= 1
3885 return matched_attributes
3887 #Macro scan_vm_pages
3888 @header("{0: >26s}{1: >20s}{2: >10s}{3: >20s}{4: >20s}{5: >16s}".format("vm_pages_index/zone", "vm_page", "q_state", "vm_object", "offset", "ppn", "bitfield", "from_zone_map"))
3889 @lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
3890 def ScanVMPages(cmd_args
=None, cmd_options
={}):
3891 """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
3892 usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
3894 scan_vm_pages -A: scan vm pages in the global vm_pages array
3895 scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
3896 scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
3897 scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
3898 scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
3899 scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
3900 scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
3901 scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
3902 scan_vm_pages <-A> -N <npages>: Scan at most npages
3904 if (len(cmd_options
) < 1):
3905 raise ArgumentError("Please specify at least one matching attribute")
3907 vm_pages
= kern
.globals.vm_pages
3908 vm_pages_count
= kern
.globals.vm_pages_count
3911 npages
= vm_pages_count
3912 scan_vmpages_array
= False
3913 scan_vmpages_zone
= False
3916 if "-A" in cmd_options
:
3917 scan_vmpages_array
= True
3919 if "-Z" in cmd_options
:
3920 scan_vmpages_zone
= True
3922 if scan_vmpages_array
== False and scan_vmpages_zone
== False:
3923 raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
3925 attribute_values
= {}
3926 if "-S" in cmd_options
:
3927 attribute_values
["vmp_q_state"] = kern
.GetValueFromAddress(cmd_options
["-S"], 'int')
3928 attribute_count
+= 1
3930 if "-O" in cmd_options
:
3931 attribute_values
["vm_object"] = kern
.GetValueFromAddress(cmd_options
["-O"], 'vm_object_t')
3932 attribute_count
+= 1
3934 if "-F" in cmd_options
:
3935 attribute_values
["vmp_offset"] = kern
.GetValueFromAddress(cmd_options
["-F"], 'unsigned long long')
3936 attribute_count
+= 1
3938 if "-P" in cmd_options
:
3939 attribute_values
["phys_page"] = kern
.GetValueFromAddress(cmd_options
["-P"], 'unsigned int')
3940 attribute_count
+= 1
3942 if "-B" in cmd_options
:
3943 valid_vmp_bitfields
= [
3944 "vmp_in_background",
3945 "vmp_on_backgroundq",
3959 "vmp_free_when_done",
3973 "vmp_written_by_kernel",
3974 "vmp_unused_object_bits"
3976 attribute_values
["bitfield"] = cmd_options
["-B"]
3977 if attribute_values
["bitfield"] in valid_vmp_bitfields
:
3978 attribute_count
+= 1
3980 raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield
))
3982 if "-I" in cmd_options
:
3983 start_index
= kern
.GetValueFromAddress(cmd_options
["-I"], 'int')
3984 npages
= vm_pages_count
- start_index
3986 if "-N" in cmd_options
:
3987 npages
= kern
.GetValueFromAddress(cmd_options
["-N"], 'int')
3989 raise ArgumentError("You specified -N 0, nothing to be scanned")
3991 end_index
= start_index
+ npages
- 1
3992 if end_index
>= vm_pages_count
:
3993 raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count
))
3995 header_after_n_lines
= 40
3996 format_string
= "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
3999 if scan_vmpages_array
:
4000 print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index
, end_index
, attribute_count
)
4002 while i
<= end_index
:
4004 if match_vm_page_attributes(page
, attribute_values
) == attribute_count
:
4005 if found_in_array
% header_after_n_lines
== 0:
4006 print ScanVMPages
.header
4008 print format_string
.format(str(i
), addressof(page
), page
.vmp_q_state
, _vm_page_unpack_ptr(page
.vmp_object
), page
.vmp_offset
, _vm_page_get_phys_page(addressof(page
)))
4014 if scan_vmpages_zone
:
4015 page_size
= kern
.GetGlobalVariable('page_size')
4016 num_zones
= kern
.GetGlobalVariable('num_zones')
4017 zone_array
= kern
.GetGlobalVariable('zone_array')
4018 print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count
)
4020 while i
< num_zones
:
4021 zone
= zone_array
[i
]
4022 if str(zone
.z_name
) == "vm pages":
4027 print "Cannot find vm_pages zone, skip the scan"
4029 print "Scanning page queues in the vm_pages zone..."
4030 elements
= FindAllocatedElementsInZone(zone
)
4031 for elem
in elements
:
4032 page
= kern
.GetValueFromAddress(elem
, 'vm_page_t')
4034 if match_vm_page_attributes(page
, attribute_values
) == attribute_count
:
4035 if found_in_zone
% header_after_n_lines
== 0:
4036 print ScanVMPages
.header
4038 vm_object
= _vm_page_unpack_ptr(page
.vmp_object
)
4039 phys_page
= _vm_page_get_phys_page(page
)
4040 print format_string
.format("vm_pages zone", elem
, page
.vmp_q_state
, vm_object
, page
.vmp_offset
, phys_page
)
4043 total
= found_in_array
+ found_in_zone
4044 print "Found {0:d} vm pages ({1:d} in array, {2:d} in zone) matching the requested {3:d} attribute(s)".format(total
, found_in_array
, found_in_zone
, attribute_count
)
4046 #EndMacro scan_vm_pages
4048 VM_PAGE_IS_WIRED
= 1
4050 @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"))
4051 @lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
4052 def VMObjectWalkPages(cmd_args
=None, cmd_options
={}):
4053 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
4054 specifically look for this page, highlighting it in the output or noting if it was not found. For
4055 each page, we confirm that it points to the object. We also keep track of the number of pages we
4056 see and compare this to the object's resident page count field.
4058 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
4059 vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
4060 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
4061 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
4062 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)
4063 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
4064 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
4065 vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
4069 if (cmd_args
== None or len(cmd_args
) < 1):
4070 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
4074 obj
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
4077 if "-P" in cmd_options
:
4078 page
= kern
.GetValueFromAddress(cmd_options
['-P'], 'vm_page_t')
4081 if "-O" in cmd_options
:
4082 off
= kern
.GetValueFromAddress(cmd_options
['-O'], 'vm_offset_t')
4085 if "-S" in cmd_options
:
4086 if page
== 0 and off
< 0:
4087 raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
4090 walk_backwards
= False
4091 if "-B" in cmd_options
:
4092 walk_backwards
= True
4095 if "-Q" in cmd_options
:
4099 print VMObjectWalkPages
.header
4100 format_string
= "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
4101 first_bitfield_format_string
= "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
4102 second_bitfield_format_string
= "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
4103 second_bitfield_format_string
+= "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
4104 second_bitfield_format_string
+= "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
4105 second_bitfield_format_string
+= "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
4107 limit
= 4096 #arbitrary limit of number of pages to walk
4109 if "-N" in cmd_options
:
4113 if "-C" in cmd_options
:
4117 res_page_count
= unsigned(obj
.resident_page_count
)
4121 for vmp
in IterateQueue(obj
.memq
, "vm_page_t", "vmp_listq", walk_backwards
, unpack_ptr_fn
=_vm_page_unpack_ptr
):
4124 if (page
!= 0 and not(page_found
) and vmp
== page
):
4125 out_string
+= "******"
4128 if (off
> 0 and not(page_found
) and vmp
.vmp_offset
== off
):
4129 out_string
+= "******"
4132 if page
!= 0 or off
> 0 or quiet_mode
:
4133 if (page_count
% 1000) == 0:
4134 print "traversed %d pages ...\n" % (page_count
)
4136 out_string
+= format_string
.format(page_count
, res_page_count
, vmp
, vmp
.vmp_offset
, _vm_page_unpack_ptr(vmp
.vmp_listq
.next
), _vm_page_get_phys_page(vmp
), vmp
.vmp_wire_count
)
4137 out_string
+= first_bitfield_format_string
.format(vmp
.vmp_q_state
, vmp
.vmp_in_background
, vmp
.vmp_on_backgroundq
, vmp
.vmp_gobbled
, vmp
.vmp_laundry
, vmp
.vmp_no_cache
,
4138 vmp
.vmp_private
, vmp
.vmp_reference
)
4140 if hasattr(vmp
,'slid'):
4144 out_string
+= second_bitfield_format_string
.format(vmp
.vmp_busy
, vmp
.vmp_wanted
, vmp
.vmp_tabled
, vmp
.vmp_hashed
, vmp
.vmp_fictitious
, vmp
.vmp_clustered
,
4145 vmp
.vmp_pmapped
, vmp
.vmp_xpmapped
, vmp
.vmp_wpmapped
, vmp
.vmp_free_when_done
, vmp
.vmp_absent
,
4146 vmp
.vmp_error
, vmp
.vmp_dirty
, vmp
.vmp_cleaning
, vmp
.vmp_precious
, vmp
.vmp_overwriting
,
4147 vmp
.vmp_restart
, vmp
.vmp_unusual
, 0, 0,
4148 vmp
.vmp_cs_validated
, vmp
.vmp_cs_tainted
, vmp
.vmp_cs_nx
, vmp
.vmp_reusable
, vmp
.vmp_lopage
, vmp_slid
,
4149 vmp
.vmp_written_by_kernel
)
4151 if (vmp
in pages_seen
):
4152 print out_string
+ "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " twice. stopping...\n"
4155 if (_vm_page_unpack_ptr(vmp
.vmp_object
) != unsigned(obj
)):
4156 print out_string
+ " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp
.vmp_object
)))
4159 if (vmp
.vmp_q_state
== VM_PAGE_IS_WIRED
) and (vmp
.vmp_wire_count
== 0):
4160 print out_string
+ " page in wired state with wire_count of 0\n"
4161 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + "\n"
4162 print "stopping...\n"
4165 if (hasattr(vmp
, 'vmp_unused_page_bits') and (vmp
.vmp_unused_page_bits
!= 0)):
4166 print out_string
+ " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " unused__pageq_bits: %d\n" % (vmp
.vmp_unused_page_bits
)
4167 print "stopping...\n"
4170 if (hasattr(vmp
, 'vmp_unused_object_bits') and (vmp
.vmp_unused_object_bits
!= 0)):
4171 print out_string
+ " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp
)) + " unused_object_bits : %d\n" % (vmp
.vmp_unused_object_bits
)
4172 print "stopping...\n"
4178 hash_id
= _calc_vm_page_hash(obj
, vmp
.vmp_offset
)
4179 hash_page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
4180 hash_page
= _vm_page_unpack_ptr(hash_page_list
)
4183 while (hash_page
!= 0):
4184 hash_page_t
= kern
.GetValueFromAddress(hash_page
, 'vm_page_t')
4185 if hash_page_t
== vmp
:
4187 hash_page
= _vm_page_unpack_ptr(hash_page_t
.vmp_next_m
)
4189 if (unsigned(vmp
) != unsigned(hash_page_t
)):
4190 print out_string
+ "unable to find page: " + "{0: <#020x}".format(unsigned(vmp
)) + " from object in kernel page bucket list\n"
4191 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args
[0], unsigned(vmp
.vmp_offset
)))
4194 if (page_count
>= limit
and not(ignore_limit
)):
4195 print out_string
+ "Limit reached (%d pages), stopping..." % (limit
)
4200 if page_found
and stop
:
4201 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
)))
4205 print("page found? : %s\n" % page_found
)
4208 print("page found? : %s\n" % page_found
)
4210 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
)))
4212 if show_compressed
!= 0 and obj
.pager
!= 0 and unsigned(obj
.pager
.mo_pager_ops
) == unsigned(addressof(kern
.globals.compressor_pager_ops
)):
4213 pager
= Cast(obj
.pager
, 'compressor_pager *')
4214 chunks
= pager
.cpgr_num_slots
/ 128
4215 pagesize
= kern
.globals.page_size
4218 while page_idx
< pager
.cpgr_num_slots
:
4220 chunk
= pager
.cpgr_slots
.cpgr_islots
[page_idx
/ 128]
4221 slot
= chunk
[page_idx
% 128]
4222 elif pager
.cpgr_num_slots
> 2:
4223 slot
= pager
.cpgr_slots
.cpgr_dslots
[page_idx
]
4225 slot
= pager
.cpgr_slots
.cpgr_eslots
[page_idx
]
4228 print("compressed page for offset: %x slot %x\n" % ((page_idx
* pagesize
) - obj
.paging_offset
, slot
))
4229 page_idx
= page_idx
+ 1
4232 @lldb_command("show_all_apple_protect_pagers")
4233 def ShowAllAppleProtectPagers(cmd_args
=None):
4234 """Routine to print all apple_protect pagers
4235 usage: show_all_apple_protect_pagers
4237 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "cached", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
4238 qhead
= kern
.globals.apple_protect_pager_queue
4239 qtype
= GetType('apple_protect_pager *')
4240 qcnt
= kern
.globals.apple_protect_pager_count
4242 for pager
in IterateQueue(qhead
, qtype
, "pager_queue"):
4244 show_apple_protect_pager(pager
, qcnt
, idx
)
4246 @lldb_command("show_apple_protect_pager")
4247 def ShowAppleProtectPager(cmd_args
=None):
4248 """Routine to print out info about an apple_protect pager
4249 usage: show_apple_protect_pager <pager>
4251 if cmd_args
== None or len(cmd_args
) < 1:
4252 print "Invalid argument.", ShowAppleProtectPager
.__doc
__
4254 pager
= kern
.GetValueFromAddress(cmd_args
[0], 'apple_protect_pager_t')
4255 show_apple_protect_pager(pager
, 1, 1)
4257 def show_apple_protect_pager(pager
, qcnt
, idx
):
4258 object = pager
.backing_object
4259 shadow
= object.shadow
4262 shadow
= object.shadow
4263 vnode_pager
= Cast(object.pager
,'vnode_pager *')
4264 filename
= GetVnodePath(vnode_pager
.vnode_handle
)
4265 if hasattr(pager
, "ap_pgr_hdr_ref"):
4266 refcnt
= pager
.ap_pgr_hdr_ref
4268 refcnt
= pager
.ap_pgr_hdr
.mo_ref
4269 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{: <#018x} <decrypt:{: <#018x} end:{:#018x} ops:{: <#018x} refs:{:<d}>\n\tvnode:{: <#018x} {:s}\n".format(idx
, qcnt
, pager
, refcnt
, pager
.is_ready
, pager
.is_mapped
, pager
.is_cached
, pager
.backing_object
, pager
.backing_offset
, pager
.crypto_backing_offset
, pager
.crypto_start
, pager
.crypto_end
, pager
.crypt_info
, pager
.crypt_info
.page_decrypt
, pager
.crypt_info
.crypt_end
, pager
.crypt_info
.crypt_ops
, pager
.crypt_info
.crypt_refcnt
, vnode_pager
.vnode_handle
, filename
)
4270 showvmobject(pager
.backing_object
, pager
.backing_offset
, pager
.crypto_end
- pager
.crypto_start
, 1, 1)
4272 @lldb_command("show_all_shared_region_pagers")
4273 def ShowAllSharedRegionPagers(cmd_args
=None):
4274 """Routine to print all shared_region pagers
4275 usage: show_all_shared_region_pagers
4277 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "object", "offset", "jop_key", "slide", "slide_info")
4278 qhead
= kern
.globals.shared_region_pager_queue
4279 qtype
= GetType('shared_region_pager *')
4280 qcnt
= kern
.globals.shared_region_pager_count
4282 for pager
in IterateQueue(qhead
, qtype
, "srp_queue"):
4284 show_shared_region_pager(pager
, qcnt
, idx
)
4286 @lldb_command("show_shared_region_pager")
4287 def ShowSharedRegionPager(cmd_args
=None):
4288 """Routine to print out info about a shared_region pager
4289 usage: show_shared_region_pager <pager>
4291 if cmd_args
== None or len(cmd_args
) < 1:
4292 print "Invalid argument.", ShowSharedRegionPager
.__doc
__
4294 pager
= kern
.GetValueFromAddress(cmd_args
[0], 'shared_region_pager_t')
4295 show_shared_region_pager(pager
, 1, 1)
4297 def show_shared_region_pager(pager
, qcnt
, idx
):
4298 object = pager
.srp_backing_object
4299 shadow
= object.shadow
4302 shadow
= object.shadow
4303 vnode_pager
= Cast(object.pager
,'vnode_pager *')
4304 filename
= GetVnodePath(vnode_pager
.vnode_handle
)
4305 if hasattr(pager
, 'srp_ref_count'):
4306 ref_count
= pager
.srp_ref_count
4308 ref_count
= pager
.srp_header
.mo_ref
4309 if hasattr(pager
, 'srp_jop_key'):
4310 jop_key
= pager
.srp_jop_key
4313 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {:#018x} {:#018x} {:#018x}\n\tvnode:{: <#018x} {:s}\n".format(idx
, qcnt
, pager
, ref_count
, pager
.srp_is_ready
, pager
.srp_is_mapped
, pager
.srp_backing_object
, pager
.srp_backing_offset
, jop_key
, pager
.srp_slide_info
.si_slide
, pager
.srp_slide_info
, vnode_pager
.vnode_handle
, filename
)
4314 showvmobject(pager
.srp_backing_object
, pager
.srp_backing_offset
, pager
.srp_slide_info
.si_end
- pager
.srp_slide_info
.si_start
, 1, 1)
4316 @lldb_command("show_console_ring")
4317 def ShowConsoleRingData(cmd_args
=None):
4318 """ Print console ring buffer stats and data
4320 cr
= kern
.globals.console_ring
4321 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
)
4323 for i
in range(unsigned(cr
.used
)):
4324 idx
= ((unsigned(cr
.read_ptr
) - unsigned(cr
.buffer)) + i
) % unsigned(cr
.len)
4325 pending_data
.append("{:c}".format(cr
.buffer[idx
]))
4329 print "".join(pending_data
)
4331 # Macro: showjetsamsnapshot
4333 @lldb_command("showjetsamsnapshot", "DA")
4334 def ShowJetsamSnapshot(cmd_args
=None, cmd_options
={}):
4335 """ Dump entries in the jetsam snapshot table
4336 usage: showjetsamsnapshot [-D] [-A]
4337 Use -D flag to print extra physfootprint details
4338 Use -A flag to print all entries (regardless of valid count)
4341 # Not shown are uuid, user_data, cpu_time
4345 show_footprint_details
= False
4346 show_all_entries
= False
4348 if "-D" in cmd_options
:
4349 show_footprint_details
= True
4351 if "-A" in cmd_options
:
4352 show_all_entries
= True
4354 valid_count
= kern
.globals.memorystatus_jetsam_snapshot_count
4355 max_count
= kern
.globals.memorystatus_jetsam_snapshot_max
4357 if (show_all_entries
== True):
4362 print "{:s}".format(valid_count
)
4363 print "{:s}".format(max_count
)
4366 print "The jetsam snapshot is empty."
4367 print "Use -A to force dump all entries (regardless of valid count)"
4370 # Dumps the snapshot header info
4371 print lldb_run_command('p *memorystatus_jetsam_snapshot')
4373 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}"
4374 if (show_footprint_details
== True):
4375 hdr_format
+= "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
4378 if (show_footprint_details
== False):
4379 print hdr_format
.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
4380 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
4382 print hdr_format
.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
4383 print hdr_format
.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
4386 entry_format
= "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
4387 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
4388 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
4389 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
4390 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
4392 if (show_footprint_details
== True):
4393 entry_format
+= "{e.jse_internal_pages: >15d} "\
4394 "{e.jse_internal_compressed_pages: >15d} "\
4395 "{e.jse_iokit_mapped_pages: >12d} "\
4396 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
4397 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
4398 "{e.jse_alternate_accounting_pages: >10d} "\
4399 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
4400 "{e.jse_page_table_pages: >10d}"
4402 snapshot_list
= kern
.globals.memorystatus_jetsam_snapshot
.entries
4405 current_entry
= dereference(Cast(addressof(snapshot_list
[idx
]), 'jetsam_snapshot_entry *'))
4406 print entry_format
.format(index
=idx
, e
=current_entry
)
4410 # EndMacro: showjetsamsnapshot
4412 # Macro: showvnodecleanblk/showvnodedirtyblk
4414 def _GetBufSummary(buf
):
4415 """ Get a summary of important information out of a buf_t.
4417 initial
= "(struct buf) {0: <#0x} ="
4419 # List all of the fields in this buf summary.
4420 entries
= [buf
.b_hash
, buf
.b_vnbufs
, buf
.b_freelist
, buf
.b_timestamp
, buf
.b_whichq
,
4421 buf
.b_flags
, buf
.b_lflags
, buf
.b_error
, buf
.b_bufsize
, buf
.b_bcount
, buf
.b_resid
,
4422 buf
.b_dev
, buf
.b_datap
, buf
.b_lblkno
, buf
.b_blkno
, buf
.b_iodone
, buf
.b_vp
,
4423 buf
.b_rcred
, buf
.b_wcred
, buf
.b_upl
, buf
.b_real_bp
, buf
.b_act
, buf
.b_drvdata
,
4424 buf
.b_fsprivate
, buf
.b_transaction
, buf
.b_dirtyoff
, buf
.b_dirtyend
, buf
.b_validoff
,
4425 buf
.b_validend
, buf
.b_redundancy_flags
, buf
.b_proc
, buf
.b_attr
]
4427 # Join an (already decent) string representation of each field
4428 # with newlines and indent the region.
4429 joined_strs
= "\n".join([str(i
).rstrip() for i
in entries
]).replace('\n', "\n ")
4431 # Add the total string representation to our title and return it.
4432 out_str
= initial
.format(int(buf
)) + " {\n " + joined_strs + "\n}\n\n"
4435 def _ShowVnodeBlocks(dirty
=True, cmd_args
=None):
4436 """ Display info about all [dirty|clean] blocks in a vnode.
4438 if cmd_args
== None or len(cmd_args
) < 1:
4439 print "Please provide a valid vnode argument."
4442 vnodeval
= kern
.GetValueFromAddress(cmd_args
[0], 'vnode *')
4443 list_head
= vnodeval
.v_cleanblkhd
;
4445 list_head
= vnodeval
.v_dirtyblkhd
4447 print "Blocklist for vnode {}:".format(cmd_args
[0])
4450 for buf
in IterateListEntry(list_head
, 'struct buf *', 'b_hash'):
4451 # For each block (buf_t) in the appropriate list,
4452 # ask for a summary and print it.
4453 print "---->\nblock {}: ".format(i
) + _GetBufSummary(buf
)
4457 @lldb_command('showvnodecleanblk')
4458 def ShowVnodeCleanBlocks(cmd_args
=None):
4459 """ Display info about all clean blocks in a vnode.
4460 usage: showvnodecleanblk <address of vnode>
4462 _ShowVnodeBlocks(False, cmd_args
)
4464 @lldb_command('showvnodedirtyblk')
4465 def ShowVnodeDirtyBlocks(cmd_args
=None):
4466 """ Display info about all dirty blocks in a vnode.
4467 usage: showvnodedirtyblk <address of vnode>
4469 _ShowVnodeBlocks(True, cmd_args
)
4471 # EndMacro: showvnodecleanblk/showvnodedirtyblk
4474 @lldb_command("vm_page_lookup_in_map")
4475 def VmPageLookupInMap(cmd_args
=None):
4476 """Lookup up a page at a virtual address in a VM map
4477 usage: vm_page_lookup_in_map <map> <vaddr>
4479 if cmd_args
== None or len(cmd_args
) < 2:
4480 print "Invalid argument.", VmPageLookupInMap
.__doc
__
4482 map = kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
4483 vaddr
= kern
.GetValueFromAddress(cmd_args
[1], 'vm_map_offset_t')
4484 print "vaddr {:#018x} in map {: <#018x}".format(vaddr
, map)
4485 vm_page_lookup_in_map(map, vaddr
)
4487 def vm_page_lookup_in_map(map, vaddr
):
4488 vaddr
= unsigned(vaddr
)
4489 vme_list_head
= map.hdr
.links
4490 vme_ptr_type
= GetType('vm_map_entry *')
4491 for vme
in IterateQueue(vme_list_head
, vme_ptr_type
, "links"):
4492 if unsigned(vme
.links
.start
) > vaddr
:
4494 if unsigned(vme
.links
.end
) <= vaddr
:
4496 offset_in_vme
= vaddr
- unsigned(vme
.links
.start
)
4497 print " offset {:#018x} in map entry {: <#018x} [{:#018x}:{:#018x}] object {: <#018x} offset {:#018x}".format(offset_in_vme
, vme
, unsigned(vme
.links
.start
), unsigned(vme
.links
.end
), vme
.vme_object
.vmo_object
, unsigned(vme
.vme_offset
) & ~
0xFFF)
4498 offset_in_object
= offset_in_vme
+ (unsigned(vme
.vme_offset
) & ~
0xFFF)
4500 print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object
, vme
.vme_object
.vmo_submap
)
4501 vm_page_lookup_in_map(vme
.vme_object
.vmo_submap
, offset_in_object
)
4503 vm_page_lookup_in_object(vme
.vme_object
.vmo_object
, offset_in_object
)
4505 @lldb_command("vm_page_lookup_in_object")
4506 def VmPageLookupInObject(cmd_args
=None):
4507 """Lookup up a page at a given offset in a VM object
4508 usage: vm_page_lookup_in_object <object> <offset>
4510 if cmd_args
== None or len(cmd_args
) < 2:
4511 print "Invalid argument.", VmPageLookupInObject
.__doc
__
4513 object = kern
.GetValueFromAddress(cmd_args
[0], 'vm_object_t')
4514 offset
= kern
.GetValueFromAddress(cmd_args
[1], 'vm_object_offset_t')
4515 print "offset {:#018x} in object {: <#018x}".format(offset
, object)
4516 vm_page_lookup_in_object(object, offset
)
4518 def vm_page_lookup_in_object(object, offset
):
4519 offset
= unsigned(offset
)
4520 page_size
= kern
.globals.page_size
4521 trunc_offset
= offset
& ~
(page_size
- 1)
4522 print " offset {:#018x} in VM object {: <#018x}".format(offset
, object)
4523 hash_id
= _calc_vm_page_hash(object, trunc_offset
)
4524 page_list
= kern
.globals.vm_page_buckets
[hash_id
].page_list
4525 page
= _vm_page_unpack_ptr(page_list
)
4527 m
= kern
.GetValueFromAddress(page
, 'vm_page_t')
4528 m_object_val
= _vm_page_unpack_ptr(m
.vmp_object
)
4529 m_object
= kern
.GetValueFromAddress(m_object_val
, 'vm_object_t')
4530 if unsigned(m_object
) != unsigned(object) or unsigned(m
.vmp_offset
) != unsigned(trunc_offset
):
4531 page
= _vm_page_unpack_ptr(m
.vmp_next_m
)
4533 print " resident page {: <#018x} phys {:#010x}".format(m
, _vm_page_get_phys_page(m
))
4535 if object.pager
and object.pager_ready
:
4536 offset_in_pager
= trunc_offset
+ unsigned(object.paging_offset
)
4537 if not object.internal
:
4538 print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager
, object.pager
.mo_pager_ops
.memory_object_pager_name
, object.pager
)
4540 pager
= Cast(object.pager
, 'compressor_pager *')
4541 ret
= vm_page_lookup_in_compressor_pager(pager
, offset_in_pager
)
4544 if object.shadow
and not object.phys_contiguous
:
4545 offset_in_shadow
= offset
+ unsigned(object.vo_un2
.vou_shadow_offset
)
4546 vm_page_lookup_in_object(object.shadow
, offset_in_shadow
)
4548 print " page is absent and will be zero-filled on demand"
4551 @lldb_command("vm_page_lookup_in_compressor_pager")
4552 def VmPageLookupInCompressorPager(cmd_args
=None):
4553 """Lookup up a page at a given offset in a compressor pager
4554 usage: vm_page_lookup_in_compressor_pager <pager> <offset>
4556 if cmd_args
== None or len(cmd_args
) < 2:
4557 print "Invalid argument.", VmPageLookupInCompressorPager
.__doc
__
4559 pager
= kern
.GetValueFromAddress(cmd_args
[0], 'compressor_pager_t')
4560 offset
= kern
.GetValueFromAddress(cmd_args
[1], 'memory_object_offset_t')
4561 print "offset {:#018x} in compressor pager {: <#018x}".format(offset
, pager
)
4562 vm_page_lookup_in_compressor_pager(pager
, offset
)
4564 def vm_page_lookup_in_compressor_pager(pager
, offset
):
4565 offset
= unsigned(offset
)
4566 page_size
= unsigned(kern
.globals.page_size
)
4567 page_num
= unsigned(offset
/ page_size
)
4568 if page_num
> pager
.cpgr_num_slots
:
4569 print " *** ERROR: vm_page_lookup_in_compressor_pager({: <#018x},{:#018x}): page_num {:#x} > num_slots {:#x}".format(pager
, offset
, page_num
, pager
.cpgr_num_slots
)
4571 slots_per_chunk
= 512 / sizeof ('compressor_slot_t')
4572 num_chunks
= unsigned((pager
.cpgr_num_slots
+slots_per_chunk
-1) / slots_per_chunk
)
4574 chunk_idx
= unsigned(page_num
/ slots_per_chunk
)
4575 chunk
= pager
.cpgr_slots
.cpgr_islots
[chunk_idx
]
4576 slot_idx
= unsigned(page_num
% slots_per_chunk
)
4577 slot
= GetObjectAtIndexFromArray(chunk
, slot_idx
)
4578 slot_str
= "islots[{:d}][{:d}]".format(chunk_idx
, slot_idx
)
4579 elif pager
.cpgr_num_slots
> 2:
4581 slot
= GetObjectAtIndexFromArray(pager
.cpgr_slots
.cpgr_dslots
, slot_idx
)
4582 slot_str
= "dslots[{:d}]".format(slot_idx
)
4585 slot
= GetObjectAtIndexFromArray(pager
.cpgr_slots
.cpgr_eslots
, slot_idx
)
4586 slot_str
= "eslots[{:d}]".format(slot_idx
)
4587 print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset
, pager
, slot_str
, slot
)
4590 slot_value
= dereference(slot
)
4591 print " value {:#010x}".format(slot_value
)
4592 vm_page_lookup_in_compressor(Cast(slot
, 'c_slot_mapping_t'))
4595 @lldb_command("vm_page_lookup_in_compressor")
4596 def VmPageLookupInCompressor(cmd_args
=None):
4597 """Lookup up a page in a given compressor slot
4598 usage: vm_page_lookup_in_compressor <slot>
4600 if cmd_args
== None or len(cmd_args
) < 1:
4601 print "Invalid argument.", VmPageLookupInCompressor
.__doc
__
4603 slot
= kern
.GetValueFromAddress(cmd_args
[0], 'compressor_slot_t *')
4604 print "compressor slot {: <#018x}".format(slot
)
4605 vm_page_lookup_in_compressor(slot
)
4607 C_SV_CSEG_ID
= ((1 << 22) - 1)
4609 def vm_page_lookup_in_compressor(slot_ptr
):
4610 slot_ptr
= Cast(slot_ptr
, 'compressor_slot_t *')
4611 slot_value
= dereference(slot_ptr
)
4612 slot
= Cast(slot_value
, 'c_slot_mapping')
4614 print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr
), unsigned(slot_value
), slot
.s_cseg
, slot
.s_cindx
)
4617 if slot
.s_cseg
== C_SV_CSEG_ID
:
4618 sv
= kern
.globals.c_segment_sv_hash_table
4619 print "single value[{:#d}]: ref {:d} value {:#010x}".format(slot
.s_cindx
, sv
[slot
.s_cindx
].c_sv_he_un
.c_sv_he
.c_sv_he_ref
, sv
[slot
.s_cindx
].c_sv_he_un
.c_sv_he
.c_sv_he_data
)
4621 if slot
.s_cseg
== 0 or unsigned(slot
.s_cseg
) > unsigned(kern
.globals.c_segments_available
):
4622 print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot
.s_cseg
, unsigned(kern
.globals.c_segments_available
))
4624 c_segments
= kern
.globals.c_segments
4625 c_segments_elt
= GetObjectAtIndexFromArray(c_segments
, slot
.s_cseg
-1)
4626 c_seg
= c_segments_elt
.c_seg
4628 if hasattr(c_seg
, 'c_state'):
4629 c_state
= c_seg
.c_state
4631 c_state_str
= "C_IS_EMPTY"
4634 c_state_str
= "C_IS_FREE"
4637 c_state_str
= "C_IS_FILLING"
4639 c_state_str
= "C_ON_AGE_Q"
4641 c_state_str
= "C_ON_SWAPOUT_Q"
4643 c_state_str
= "C_ON_SWAPPEDOUT_Q"
4646 c_state_str
= "C_ON_SWAPPEDOUTSPARSE_Q"
4649 c_state_str
= "C_ON_SWAPPEDIN_Q"
4651 c_state_str
= "C_ON_MAJORCOMPACT_Q"
4653 c_state_str
= "C_ON_BAD_Q"
4656 c_state_str
= "<unknown>"
4659 c_state_str
= "<no c_state field>"
4660 print "c_segments[{:d}] {: <#018x} c_seg {: <#018x} c_state {:#x}={:s}".format(slot
.s_cseg
-1, c_segments_elt
, c_seg
, c_state
, c_state_str
)
4661 c_indx
= unsigned(slot
.s_cindx
)
4662 if hasattr(c_seg
, 'c_slot_var_array'):
4663 c_seg_fixed_array_len
= kern
.globals.c_seg_fixed_array_len
4664 if c_indx
< c_seg_fixed_array_len
:
4665 cs
= c_seg
.c_slot_fixed_array
[c_indx
]
4667 cs
= GetObjectAtIndexFromArray(c_seg
.c_slot_var_array
, c_indx
- c_seg_fixed_array_len
)
4669 C_SEG_SLOT_ARRAY_SIZE
= 64
4670 C_SEG_SLOT_ARRAY_MASK
= C_SEG_SLOT_ARRAY_SIZE
- 1
4671 cs
= GetObjectAtIndexFromArray(c_seg
.c_slots
[c_indx
/ C_SEG_SLOT_ARRAY_SIZE
], c_indx
& C_SEG_SLOT_ARRAY_MASK
)
4673 c_slot_unpacked_ptr
= vm_unpack_ptr(cs
.c_packed_ptr
, kern
.globals.c_slot_packing_params
)
4674 print "c_slot {: <#018x} c_offset {:#x} c_size {:#x} c_packed_ptr {:#x} (unpacked: {: <#018x})".format(cs
, cs
.c_offset
, cs
.c_size
, cs
.c_packed_ptr
, unsigned(c_slot_unpacked_ptr
))
4675 if unsigned(slot_ptr
) != unsigned(c_slot_unpacked_ptr
):
4676 print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr
, c_slot_unpacked_ptr
)
4678 c_data
= c_seg
.c_store
.c_buffer
+ (4 * cs
.c_offset
)
4680 cmd
= "memory read {: <#018x} {: <#018x} --force".format(c_data
, c_data
+ c_size
)
4682 print lldb_run_command(cmd
)
4684 print "<no compressed data>"
4686 @lldb_command('vm_scan_all_pages')
4687 def VMScanAllPages(cmd_args
=None):
4688 """Scans the vm_pages[] array
4690 vm_pages_count
= kern
.globals.vm_pages_count
4691 vm_pages
= kern
.globals.vm_pages
4694 local_free_count
= 0
4696 local_active_count
= 0
4698 speculative_count
= 0
4701 compressor_count
= 0
4702 pageable_internal_count
= 0
4703 pageable_external_count
= 0
4705 secluded_free_count
= 0
4706 secluded_inuse_count
= 0
4709 while i
< vm_pages_count
:
4712 print "{:d}/{:d}...\n".format(i
,vm_pages_count
)
4718 m_object_val
= _vm_page_unpack_ptr(m
.vmp_object
)
4721 if m_object
.internal
:
4726 if m
.vmp_wire_count
!= 0 and m
.vmp_local
== 0:
4727 wired_count
= wired_count
+ 1
4729 elif m
.vmp_throttled
:
4730 throttled_count
= throttled_count
+ 1
4733 active_count
= active_count
+ 1
4736 local_active_count
= local_active_count
+ 1
4738 elif m
.vmp_inactive
:
4739 inactive_count
= inactive_count
+ 1
4741 elif m
.vmp_speculative
:
4742 speculative_count
= speculative_count
+ 1
4745 free_count
= free_count
+ 1
4747 elif m
.vmp_secluded
:
4748 secluded_count
= secluded_count
+ 1
4750 secluded_free_count
= secluded_free_count
+ 1
4752 secluded_inuse_count
= secluded_inuse_count
+ 1
4754 elif m_object
== 0 and m
.vmp_busy
:
4755 local_free_count
= local_free_count
+ 1
4757 elif m
.vmp_compressor
:
4758 compressor_count
= compressor_count
+ 1
4761 print "weird page vm_pages[{:d}]?\n".format(i
)
4766 pageable_internal_count
= pageable_internal_count
+ 1
4768 pageable_external_count
= pageable_external_count
+ 1
4771 print "vm_pages_count = {:d}\n".format(vm_pages_count
)
4773 print "wired_count = {:d}\n".format(wired_count
)
4774 print "throttled_count = {:d}\n".format(throttled_count
)
4775 print "active_count = {:d}\n".format(active_count
)
4776 print "local_active_count = {:d}\n".format(local_active_count
)
4777 print "inactive_count = {:d}\n".format(inactive_count
)
4778 print "speculative_count = {:d}\n".format(speculative_count
)
4779 print "free_count = {:d}\n".format(free_count
)
4780 print "local_free_count = {:d}\n".format(local_free_count
)
4781 print "compressor_count = {:d}\n".format(compressor_count
)
4783 print "pageable_internal_count = {:d}\n".format(pageable_internal_count
)
4784 print "pageable_external_count = {:d}\n".format(pageable_external_count
)
4785 print "secluded_count = {:d}\n".format(secluded_count
)
4786 print "secluded_free_count = {:d}\n".format(secluded_free_count
)
4787 print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count
)
4790 @lldb_command('show_all_vm_named_entries')
4791 def ShowAllVMNamedEntries(cmd_args
=None):
4792 """ Routine to print a summary listing of all the VM named entries
4794 queue_len
= kern
.globals.vm_named_entry_count
4795 queue_head
= kern
.globals.vm_named_entry_list
4797 print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern
.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len
)
4799 # print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
4801 for entry
in IterateQueue(queue_head
, 'struct vm_named_entry *', 'named_entry_list'):
4803 showmemoryentry(entry
, idx
, queue_len
)
4805 @lldb_command('show_vm_named_entry')
4806 def ShowVMNamedEntry(cmd_args
=None):
4807 """ Routine to print a VM named entry
4809 if cmd_args
== None or len(cmd_args
) < 1:
4810 print "Invalid argument.", ShowMapVMNamedEntry
.__doc
__
4812 named_entry
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_named_entry_t')
4813 showmemoryentry(named_entry
, 0, 0)
4815 def showmemoryentry(entry
, idx
=0, queue_len
=0):
4816 """ Routine to print out a summary a VM memory entry
4818 entry - core.value : a object of type 'struct vm_named_entry *'
4822 show_pager_info
= True
4823 show_all_shadows
= True
4826 if entry
.is_sub_map
== 1:
4828 if entry
.is_copy
== 1:
4830 if entry
.is_object
== 1:
4832 if entry
.is_sub_map
== 0 and entry
.is_copy
== 0 and entry
.is_object
== 0:
4833 backing
+= "***?***"
4835 if entry
.protection
& 0x1:
4839 if entry
.protection
& 0x2:
4843 if entry
.protection
& 0x4:
4848 if hasattr(entry
, 'named_entry_alias'):
4849 extra_str
+= " alias={:d}".format(entry
.named_entry_alias
)
4850 if hasattr(entry
, 'named_entry_port'):
4851 extra_str
+= " port={:#016x}".format(entry
.named_entry_port
)
4852 print "{:d}/{:d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx
,queue_len
,entry
,entry
.ref_count
,entry
.protection
,prot
,backing
,entry
.backing
.copy
,entry
.offset
,entry
.data_offset
,entry
.size
,extra_str
)
4853 if entry
.is_sub_map
== 1:
4854 showmapvme(entry
.backing
.map, 0, 0, show_pager_info
, show_all_shadows
)
4855 elif entry
.is_copy
== 1:
4856 showmapcopyvme(entry
.backing
.copy
, 0, 0, show_pager_info
, show_all_shadows
, 0)
4857 elif entry
.is_object
== 1:
4858 showmapcopyvme(entry
.backing
.copy
, 0, 0, show_pager_info
, show_all_shadows
, 0)
4860 print "***** UNKNOWN TYPE *****"
4864 def IterateRBTreeEntry2(element
, element_type
, field_name1
, field_name2
):
4865 """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
4866 element - value : Value object for rbh_root
4867 element_type - str : Type of the link element
4868 field_name - str : Name of the field in link element's structure
4870 A generator does not return. It is used for iterating
4871 value : an object thats of type (element_type) head->sle_next. Always a pointer object
4873 elt
= element
.__getattr
__('rbh_root')
4874 if type(element_type
) == str:
4875 element_type
= gettype(element_type
)
4876 charp_type
= gettype('char *');
4880 while unsigned(elt
) != 0:
4882 elt
= cast(elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4886 while unsigned(elt
) != 0:
4888 # implementation cribbed from RB_NEXT in libkern/tree.h
4889 right
= cast(elt
.__getattr
__(field_name1
).__getattr
__(fieldname2
).__getattr
__('rbe_right'), element_type
)
4890 if unsigned(right
) != 0:
4892 left
= cast(elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4893 while unsigned(left
) != 0:
4895 left
= cast(elt
.__getattr
__(field_name1
).__getattr
(__field_name2
).__getattr
__('rbe_left'), element_type
)
4898 # avoid using GetValueFromAddress
4899 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4900 parent
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4901 parent
= cast(parent
, element_type
)
4903 if unsigned(parent
) != 0:
4904 left
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_left'), element_type
)
4905 if (unsigned(parent
) != 0) and (unsigned(elt
) == unsigned(left
)):
4908 if unsigned(parent
) != 0:
4909 right
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_right'), element_type
)
4910 while unsigned(parent
) != 0 and (unsigned(elt
) == unsigned(right
)):
4913 # avoid using GetValueFromAddress
4914 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4915 parent
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4916 parent
= cast(parent
, element_type
)
4918 right
= cast(parent
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_right'), element_type
)
4920 # avoid using GetValueFromAddress
4921 addr
= elt
.__getattr
__(field_name1
).__getattr
__(field_name2
).__getattr
__('rbe_parent')&~
1
4922 elt
= value(elt
.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr
)))
4923 elt
= cast(elt
, element_type
)
4926 @lldb_command("showmaprb")
4927 def ShowMapRB(cmd_args
=None):
4928 """Routine to print out a VM map's RB tree
4929 usage: showmaprb <vm_map>
4931 if cmd_args
== None or len(cmd_args
) < 1:
4932 print "Invalid argument.", ShowMapRB
.__doc
__
4934 map_val
= kern
.GetValueFromAddress(cmd_args
[0], 'vm_map_t')
4935 print GetVMMapSummary
.header
4936 print GetVMMapSummary(map_val
)
4937 vme_rb_root
= map_val
.hdr
.rb_head_store
4938 vme_ptr_type
= GetType('struct vm_map_entry *')
4939 print GetVMEntrySummary
.header
4940 for vme
in IterateRBTreeEntry2(vme_rb_root
, 'struct vm_map_entry *', 'store', 'entry'):
4941 print GetVMEntrySummary(vme
)
4944 @lldb_command('show_all_owned_objects', 'T')
4945 def ShowAllOwnedObjects(cmd_args
=None, cmd_options
={}):
4946 """ Routine to print the list of VM objects owned by each task
4947 -T: show only ledger-tagged objects
4949 showonlytagged
= False
4950 if "-T" in cmd_options
:
4951 showonlytagged
= True
4952 for task
in kern
.tasks
:
4953 ShowTaskOwnedVmObjects(task
, showonlytagged
)
4955 @lldb_command('show_task_owned_objects', 'T')
4956 def ShowTaskOwnedObjects(cmd_args
=None, cmd_options
={}):
4957 """ Routine to print the list of VM objects owned by the specified task
4958 -T: show only ledger-tagged objects
4960 showonlytagged
= False
4961 if "-T" in cmd_options
:
4962 showonlytagged
= True
4963 task
= kern
.GetValueFromAddress(cmd_args
[0], 'task *')
4964 ShowTaskOwnedVmObjects(task
, showonlytagged
)
4966 @lldb_command('showdeviceinfo', 'J')
4967 def ShowDeviceInfo(cmd_args
=None, cmd_options
={}):
4968 """ Routine to show basic device information (model, build, ncpus, etc...)
4969 Usage: memstats [-J]
4973 if "-J" in cmd_options
:
4976 device_info
["build"] = str(kern
.globals.osversion
)
4977 device_info
["memoryConfig"] = int(kern
.globals.max_mem_actual
)
4978 device_info
["ncpu"] = int(kern
.globals.ncpu
)
4979 device_info
["pagesize"] = int(kern
.globals.page_size
)
4980 device_info
["mlockLimit"] = long(kern
.globals.vm_global_user_wire_limit
)
4984 print json
.dumps(device_info
)
4986 PrettyPrintDictionary(device_info
)
4988 def ShowTaskOwnedVmObjects(task
, showonlytagged
=False):
4989 """ Routine to print out a summary listing of all the entries in a vm_map
4991 task - core.value : a object of type 'task *'
4995 taskobjq_total
= lambda:None
4996 taskobjq_total
.objects
= 0
4997 taskobjq_total
.vsize
= 0
4998 taskobjq_total
.rsize
= 0
4999 taskobjq_total
.wsize
= 0
5000 taskobjq_total
.csize
= 0
5001 vmo_list_head
= task
.task_objq
5002 vmo_ptr_type
= GetType('vm_object *')
5004 for vmo
in IterateQueue(vmo_list_head
, vmo_ptr_type
, "task_objq"):
5006 if not showonlytagged
or vmo
.vo_ledger_tag
!= 0:
5007 if taskobjq_total
.objects
== 0:
5009 print GetTaskSummary
.header
+ ' ' + GetProcSummary
.header
5010 print GetTaskSummary(task
) + ' ' + GetProcSummary(Cast(task
.bsd_info
, 'proc *'))
5011 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>2s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tg","owner","pid","process")
5012 ShowOwnedVmObject(vmo
, idx
, 0, taskobjq_total
)
5013 if taskobjq_total
.objects
!= 0:
5014 print " total:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(taskobjq_total
.objects
, taskobjq_total
.vsize
, taskobjq_total
.rsize
, taskobjq_total
.wsize
, taskobjq_total
.csize
)
5017 def ShowOwnedVmObject(object, idx
, queue_len
, taskobjq_total
):
5018 """ Routine to print out a VM object owned by a task
5020 object - core.value : a object of type 'struct vm_object *'
5024 page_size
= kern
.globals.page_size
5025 if object.purgable
== 0:
5027 elif object.purgable
== 1:
5029 elif object.purgable
== 2:
5031 elif object.purgable
== 3:
5035 if object.pager
== 0:
5036 compressed_count
= 0
5038 compressor_pager
= Cast(object.pager
, 'compressor_pager *')
5039 compressed_count
= compressor_pager
.cpgr_num_slots_occupied
5041 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>2d} {: <#018x} {:>6d} {:<20s}\n".format(idx
,queue_len
,object,purgable
,object.ref_count
,object.vo_un1
.vou_size
/page_size
,object.resident_page_count
,object.wired_page_count
,compressed_count
, object.vo_ledger_tag
, object.vo_un2
.vou_owner
,GetProcPIDForObjectOwner(object.vo_un2
.vou_owner
),GetProcNameForObjectOwner(object.vo_un2
.vou_owner
))
5043 taskobjq_total
.objects
+= 1
5044 taskobjq_total
.vsize
+= object.vo_un1
.vou_size
/page_size
5045 taskobjq_total
.rsize
+= object.resident_page_count
5046 taskobjq_total
.wsize
+= object.wired_page_count
5047 taskobjq_total
.csize
+= compressed_count
5049 def GetProcPIDForObjectOwner(owner
):
5050 """ same as GetProcPIDForTask() but deals with -1 for a disowned object
5052 if unsigned(Cast(owner
, 'int')) == unsigned(int(0xffffffff)):
5054 return GetProcPIDForTask(owner
)
5056 def GetProcNameForObjectOwner(owner
):
5057 """ same as GetProcNameForTask() but deals with -1 for a disowned object
5059 if unsigned(Cast(owner
, 'int')) == unsigned(int(0xffffffff)):
5061 return GetProcNameForTask(owner
)
5063 def GetDescForNamedEntry(mem_entry
):
5065 out_str
+= "\t\tmem_entry {:#08x} ref:{:d} offset:{:#08x} size:{:#08x} prot{:d} backing {:#08x}".format(mem_entry
, mem_entry
.ref_count
, mem_entry
.offset
, mem_entry
.size
, mem_entry
.protection
, mem_entry
.backing
.copy
)
5066 if mem_entry
.is_sub_map
:
5067 out_str
+= " is_sub_map"
5068 elif mem_entry
.is_copy
:
5069 out_str
+= " is_copy"
5070 elif mem_entry
.is_object
:
5071 out_str
+= " is_object"