]> git.saurik.com Git - apple/xnu.git/blame - tools/lldbmacros/memory.py
xnu-6153.41.3.tar.gz
[apple/xnu.git] / tools / lldbmacros / memory.py
CommitLineData
39236c6e
A
1
2""" Please make sure you read the README file COMPLETELY BEFORE reading anything below.
fe8ab488 3 It is very critical that you read coding guidelines in Section E in README file.
39236c6e
A
4"""
5from xnu import *
fe8ab488
A
6import sys
7import shlex
39236c6e
A
8from utils import *
9import xnudefines
10from process import *
39037602 11import macho
39236c6e
A
12
13# Macro: memstats
14@lldb_command('memstats')
15def Memstats(cmd_args=None):
16 """ Prints out a summary of various memory statistics. In particular vm_page_wire_count should be greater than 2K or you are under memory pressure.
17 """
18 try:
19 print "memorystatus_level: {: >10d}".format(kern.globals.memorystatus_level)
39236c6e 20 print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages)
39037602 21 print "inuse_ptepages_count: {: >10d}".format(kern.globals.inuse_ptepages_count)
39236c6e
A
22 except ValueError:
23 pass
24 print "vm_page_throttled_count: {: >10d}".format(kern.globals.vm_page_throttled_count)
25 print "vm_page_active_count: {: >10d}".format(kern.globals.vm_page_active_count)
26 print "vm_page_inactive_count: {: >10d}".format(kern.globals.vm_page_inactive_count)
27 print "vm_page_wire_count: {: >10d}".format(kern.globals.vm_page_wire_count)
28 print "vm_page_free_count: {: >10d}".format(kern.globals.vm_page_free_count)
29 print "vm_page_purgeable_count: {: >10d}".format(kern.globals.vm_page_purgeable_count)
30 print "vm_page_inactive_target: {: >10d}".format(kern.globals.vm_page_inactive_target)
31 print "vm_page_free_target: {: >10d}".format(kern.globals.vm_page_free_target)
39037602 32
39236c6e
A
33 print "vm_page_free_reserved: {: >10d}".format(kern.globals.vm_page_free_reserved)
34
35@xnudebug_test('test_memstats')
36def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
37 """ Test the functionality of memstats command
5ba3f43e 38 returns
39236c6e 39 - False on failure
5ba3f43e 40 - True on success
39236c6e
A
41 """
42 if not isConnected:
43 print "Target is not connected. Cannot test memstats"
44 return False
45 res = lldb.SBCommandReturnObject()
46 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
47 result = res.GetOutput()
5ba3f43e 48 if result.split(":")[1].strip().find('None') == -1 :
39236c6e 49 return True
5ba3f43e 50 else:
39236c6e
A
51 return False
52
53# EndMacro: memstats
54
55# Macro: showmemorystatus
56def CalculateLedgerPeak(phys_footprint_entry):
57 """ Internal function to calculate ledger peak value for the given phys footprint entry
5ba3f43e 58 params: phys_footprint_entry - value representing struct ledger_entry *
39236c6e
A
59 return: value - representing the ledger peak for the given phys footprint entry
60 """
61 now = kern.globals.sched_tick / 20
cb323159
A
62 ledger_peak = long(phys_footprint_entry.le_credit) - long(phys_footprint_entry.le_debit)
63 if hasattr(phys_footprint_entry._le._le_max, 'le_interval_max') and (long(phys_footprint_entry._le._le_max.le_interval_max) > ledger_peak):
64 ledger_peak = long(phys_footprint_entry._le._le_max.le_interval_max)
39236c6e
A
65 return ledger_peak
66
cb323159
A
67@header("{: >8s} {: >12s} {: >12s} {: >10s} {: >10s} {: >12s} {: >14s} {: >10s} {: >12s} {: >10s} {: >10s} {: >10s} {: <20s}\n".format(
68'pid', 'effective', 'requested', 'state', 'relaunch', 'user_data', 'physical', 'iokit', 'footprint',
d9a64523 69'recent peak', 'lifemax', 'limit', 'command'))
39236c6e
A
70def GetMemoryStatusNode(proc_val):
71 """ Internal function to get memorystatus information from the given proc
72 params: proc - value representing struct proc *
73 return: str - formatted output information for proc object
74 """
75 out_str = ''
76 task_val = Cast(proc_val.task, 'task *')
77 task_ledgerp = task_val.ledger
78
79 task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem]
fe8ab488 80 task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mapped]
39236c6e
A
81 task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint]
82 page_size = kern.globals.page_size
5ba3f43e 83
cb323159
A
84 phys_mem_footprint = (long(task_physmem_footprint_ledger_entry.le_credit) - long(task_physmem_footprint_ledger_entry.le_debit)) / page_size
85 iokit_footprint = (long(task_iokit_footprint_ledger_entry.le_credit) - long(task_iokit_footprint_ledger_entry.le_debit)) / page_size
86 phys_footprint = (long(task_phys_footprint_ledger_entry.le_credit) - long(task_phys_footprint_ledger_entry.le_debit)) / page_size
87 phys_footprint_limit = long(task_phys_footprint_ledger_entry.le_limit) / page_size
39236c6e
A
88 ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
89 phys_footprint_spike = ledger_peak / page_size
cb323159 90 phys_footprint_lifetime_max = long(task_phys_footprint_ledger_entry._le._le_max.le_lifetime_max) / page_size
39236c6e 91
cb323159 92 format_string = '{0: >8d} {1: >12d} {2: >12d} {3: #011x} {4: >10d} {5: #011x} {6: >12d} {7: >10d} {8: >13d}'
39236c6e 93 out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority,
cb323159
A
94 proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_relaunch_flags,
95 proc_val.p_memstat_userdata, phys_mem_footprint, iokit_footprint, phys_footprint)
39236c6e 96 if phys_footprint != phys_footprint_spike:
5ba3f43e 97 out_str += "{: >12d}".format(phys_footprint_spike)
39236c6e 98 else:
5ba3f43e
A
99 out_str += "{: >12s}".format('-')
100
101 out_str += "{: >10d} ".format(phys_footprint_lifetime_max)
102 out_str += "{: >10d} {: <20s}\n".format(phys_footprint_limit, proc_val.p_comm)
103 return out_str
39236c6e
A
104
105@lldb_command('showmemorystatus')
106def ShowMemoryStatus(cmd_args=None):
107 """ Routine to display each entry in jetsam list with a summary of pressure statistics
108 Usage: showmemorystatus
109 """
110 bucket_index = 0
111 bucket_count = 20
112 print GetMemoryStatusNode.header
5ba3f43e
A
113 print "{: >21s} {: >12s} {: >38s} {: >10s} {: >12s} {: >10s} {: >10s}\n".format("priority", "priority", "(pages)", "(pages)", "(pages)",
114 "(pages)", "(pages)", "(pages)")
39236c6e
A
115 while bucket_index < bucket_count:
116 current_bucket = kern.globals.memstat_bucket[bucket_index]
117 current_list = current_bucket.list
118 current_proc = Cast(current_list.tqh_first, 'proc *')
119 while unsigned(current_proc) != 0:
120 print GetMemoryStatusNode(current_proc)
121 current_proc = current_proc.p_memstat_list.tqe_next
122 bucket_index += 1
123 print "\n\n"
124 Memstats()
5ba3f43e 125
39236c6e
A
126# EndMacro: showmemorystatus
127
39037602
A
128def GetRealMetadata(meta):
129 """ Get real metadata for a given metadata pointer
130 """
131 try:
5ba3f43e 132 if unsigned(meta.zindex) != 0x03FF:
39037602
A
133 return meta
134 else:
135 return kern.GetValueFromAddress(unsigned(meta) - unsigned(meta.real_metadata_offset), "struct zone_page_metadata *")
136 except:
137 return 0
138
139def GetFreeList(meta):
140 """ Get the free list pointer for a given metadata pointer
141 """
142 global kern
143 zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
144 zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
145 try:
146 if unsigned(meta.freelist_offset) == unsigned(0xffffffff):
147 return 0
148 else:
149 if (unsigned(meta) >= unsigned(zone_map_min_address)) and (unsigned(meta) < unsigned(zone_map_max_address)):
150 page_index = ((unsigned(meta) - unsigned(kern.GetGlobalVariable('zone_metadata_region_min'))) / sizeof('struct zone_page_metadata'))
151 return (unsigned(zone_map_min_address) + (kern.globals.page_size * (page_index))) + meta.freelist_offset
152 else:
153 return (unsigned(meta) + meta.freelist_offset)
154 except:
155 return 0
156
157@lldb_type_summary(['zone_page_metadata'])
158@header("{:<18s} {:<18s} {:>8s} {:>8s} {:<18s} {:<20s}".format('ZONE_METADATA', 'FREELIST', 'PG_CNT', 'FREE_CNT', 'ZONE', 'NAME'))
159def GetZoneMetadataSummary(meta):
160 """ Summarize a zone metadata object
161 params: meta - obj representing zone metadata in the kernel
162 returns: str - summary of the zone metadata
163 """
164 out_str = ""
165 global kern
166 zinfo = 0
167 try:
168 out_str += 'Metadata Description:\n' + GetZoneMetadataSummary.header + '\n'
169 meta = kern.GetValueFromAddress(meta, "struct zone_page_metadata *")
170 if unsigned(meta.zindex) == 255:
171 out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}\n".format(meta, 0, 0, 0, 0, '(fake multipage meta)')
172 meta = GetRealMetadata(meta)
173 if meta == 0:
174 return ""
175 zinfo = kern.globals.zone_array[unsigned(meta.zindex)]
176 out_str += "{:#018x} {:#018x} {:8d} {:8d} {:#018x} {:s}".format(meta, GetFreeList(meta), meta.page_count, meta.free_count, addressof(zinfo), zinfo.zone_name)
177 return out_str
178 except:
179 out_str = ""
180 return out_str
181
182@header("{:<18s} {:>18s} {:>18s} {:<18s}".format('ADDRESS', 'TYPE', 'OFFSET_IN_PG', 'METADATA'))
183def WhatIs(addr):
184 """ Information about kernel pointer
185 """
186 out_str = ""
187 global kern
188 pagesize = kern.globals.page_size
189 zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
190 zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
191 if (unsigned(addr) >= unsigned(zone_map_min_address)) and (unsigned(addr) < unsigned(zone_map_max_address)):
192 zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
193 zone_metadata_region_max = kern.GetGlobalVariable('zone_metadata_region_max')
194 if (unsigned(addr) >= unsigned(zone_metadata_region_min)) and (unsigned(addr) < unsigned(zone_metadata_region_max)):
195 metadata_offset = (unsigned(addr) - unsigned(zone_metadata_region_min)) % sizeof('struct zone_page_metadata')
196 page_offset_str = "{:d}/{:d}".format((unsigned(addr) - (unsigned(addr) & ~(pagesize - 1))), pagesize)
197 out_str += WhatIs.header + '\n'
5ba3f43e 198 out_str += "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr), "Metadata", page_offset_str, unsigned(addr) - metadata_offset)
39037602
A
199 out_str += GetZoneMetadataSummary((unsigned(addr) - metadata_offset)) + '\n\n'
200 else:
201 page_index = ((unsigned(addr) & ~(pagesize - 1)) - unsigned(zone_map_min_address)) / pagesize
202 meta = unsigned(zone_metadata_region_min) + (page_index * sizeof('struct zone_page_metadata'))
203 meta = kern.GetValueFromAddress(meta, "struct zone_page_metadata *")
204 page_meta = GetRealMetadata(meta)
205 if page_meta != 0:
206 zinfo = kern.globals.zone_array[unsigned(page_meta.zindex)]
207 page_offset_str = "{:d}/{:d}".format((unsigned(addr) - (unsigned(addr) & ~(pagesize - 1))), pagesize)
208 out_str += WhatIs.header + '\n'
209 out_str += "{:#018x} {:>18s} {:>18s} {:#018x}\n\n".format(unsigned(addr), "Element", page_offset_str, page_meta)
210 out_str += GetZoneMetadataSummary(unsigned(page_meta)) + '\n\n'
211 else:
212 out_str += "Unmapped address within the zone_map ({:#018x}-{:#018x})".format(zone_map_min_address, zone_map_max_address)
213 else:
214 out_str += "Address {:#018x} is outside the zone_map ({:#018x}-{:#018x})\n".format(addr, zone_map_min_address, zone_map_max_address)
215 print out_str
216 return
217
218@lldb_command('whatis')
219def WhatIsHelper(cmd_args=None):
220 """ Routine to show information about a kernel pointer
221 Usage: whatis <address>
222 """
223 if not cmd_args:
224 raise ArgumentError("No arguments passed")
225 addr = kern.GetValueFromAddress(cmd_args[0], 'void *')
226 WhatIs(addr)
227 print "Hexdump:\n"
228 try:
229 data_array = kern.GetValueFromAddress(unsigned(addr) - 16, "uint8_t *")
230 print_hex_data(data_array[0:48], unsigned(addr) - 16, "")
231 except:
232 pass
233 return
234
d9a64523
A
235# Macro: showzcache
236
237@lldb_type_summary(['zone','zone_t'])
238@header("{:^18s} {:<40s} {:>10s} {:>10s} {:>10s} {:>10s}".format(
239'ZONE', 'NAME', 'CACHE_ELTS', 'DEP_VALID', 'DEP_EMPTY','DEP_FULL'))
240
241def GetZoneCacheSummary(zone):
242 """ Summarize a zone's cache with important information.
243 params:
244 zone: value - obj representing a zone in kernel
245 returns:
246 str - summary of the zone's cache contents
247 """
248 out_string = ""
249 format_string = '{:#018x} {:<40s} {:>10d} {:>10s} {:>10d} {:>10d}'
250 cache_elem_count = 0
251 mag_capacity = kern.GetGlobalVariable('magazine_element_count')
252 depot_capacity = kern.GetGlobalVariable('depot_element_count')
253
254
255 if zone.__getattr__('cpu_cache_enabled') :
256 for i in range(0, kern.globals.machine_info.physical_cpu):
257 cache = zone.zcache[0].zcc_per_cpu_caches[i]
258 cache_elem_count += cache.current.zcc_magazine_index
259 cache_elem_count += cache.previous.zcc_magazine_index
260
261 if zone.zcache[0].zcc_depot_index != -1:
262 cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
263 out_string += format_string.format(zone, zone.zone_name, cache_elem_count, "Y", depot_capacity - zone.zcache[0].zcc_depot_index, zone.zcache[0].zcc_depot_index)
264 else:
265 out_string += format_string.format(zone, zone.zone_name, cache_elem_count, "N", 0, 0)
266
267 return out_string
268
269@lldb_command('showzcache')
270def ZcachePrint(cmd_args=None):
271 """ Routine to print a summary listing of all the kernel zones cache contents
272 All columns are printed in decimal
273 """
274 global kern
275 print GetZoneCacheSummary.header
276 for zval in kern.zones:
277 if zval.__getattr__('cpu_cache_enabled') :
278 print GetZoneCacheSummary(zval)
279
280# EndMacro: showzcache
281
282# Macro: showzcachecpu
283
284@lldb_type_summary(['zone','zone_t'])
285@header("{:^18s} {:40s} {:>10s} {:>10s}".format(
286'ZONE', 'NAME', 'CACHE_ELTS', 'CPU_INFO'))
287
288def GetZoneCacheCPUSummary(zone):
289 """ Summarize a zone's cache broken up per cpu
290 params:
291 zone: value - obj representing a zone in kernel
292 returns:
293 str - summary of the zone's per CPU cache contents
294 """
295 out_string = ""
296 format_string = '{:#018x} {:40s} {:10d} {cpuinfo:s}'
297 cache_elem_count = 0
298 cpu_info = ""
299 per_cpu_count = 0
300 mag_capacity = kern.GetGlobalVariable('magazine_element_count')
301 depot_capacity = kern.GetGlobalVariable('depot_element_count')
302
303
304 if zone.__getattr__('cpu_cache_enabled') :
305 for i in range(0, kern.globals.machine_info.physical_cpu):
306 if i != 0:
307 cpu_info += ", "
308 cache = zone.zcache[0].zcc_per_cpu_caches[i]
309 per_cpu_count = cache.current.zcc_magazine_index
310 per_cpu_count += cache.previous.zcc_magazine_index
311 cache_elem_count += per_cpu_count
312 cpu_info += "CPU {:d}: {:5}".format(i,per_cpu_count)
313 if zone.zcache[0].zcc_depot_index != -1:
314 cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
315
316 out_string += format_string.format(zone, zone.zone_name, cache_elem_count,cpuinfo = cpu_info)
317
318 return out_string
319
320@lldb_command('showzcachecpu')
321def ZcacheCPUPrint(cmd_args=None):
322 """ Routine to print a summary listing of all the kernel zones cache contents
323 All columns are printed in decimal
324 """
325 global kern
326 print GetZoneCacheCPUSummary.header
327 for zval in kern.zones:
328 if zval.__getattr__('cpu_cache_enabled') :
329 print GetZoneCacheCPUSummary(zval)
330
331# EndMacro: showzcachecpu
332
39236c6e
A
333# Macro: zprint
334
335@lldb_type_summary(['zone','zone_t'])
cb323159
A
336@header(("{:<18s} {:_^23s} {:_^24s} {:_^13s} {:_^31s}\n"+
337"{:<18s} {:>11s} {:>11s} {:>8s} {:>7s} {:>7s} {:>6s} {:>6s} {:>7s} {:>5s} {:>3s} {:>5s} {:>7s} {:<15s} {:<20s}").format(
338'', 'SIZE (bytes)', 'ELEMENTS (#)', 'PAGES', 'ALLOC CHUNK CONFIG',
339'ZONE', 'ALLOC', 'FREE', 'ALLOC', 'FREE', 'CACHE', 'COUNT', 'FREE', 'SIZE', 'ELTS', 'PGS', 'WASTE', 'ELT_SZ', 'FLAGS', 'NAME'))
39236c6e
A
340def GetZoneSummary(zone):
341 """ Summarize a zone with important information. See help zprint for description of each field
5ba3f43e 342 params:
39236c6e 343 zone: value - obj representing a zone in kernel
5ba3f43e 344 returns:
39236c6e
A
345 str - summary of the zone
346 """
347 out_string = ""
cb323159 348 format_string = '{zone:#018x} {zone.cur_size:11,d} {free_size:11,d} {zone.count:8,d} {zone.countfree:7,d} {cache_elem_count:7,d} {zone.page_count:6,d} {zone.count_all_free_pages:6,d} {zone.alloc_size:7,d} {alloc_count:5,d} {alloc_pages:3,d} {alloc_waste:5,d} {zone.elem_size:7,d} {markings:<15s} {zone.zone_name:<20s} '
3e170ce0 349 pagesize = kern.globals.page_size
5ba3f43e 350
cb323159 351 free_size = zone.countfree * zone.elem_size
d9a64523 352 mag_capacity = kern.GetGlobalVariable('magazine_element_count')
5ba3f43e 353
39236c6e 354 alloc_pages = zone.alloc_size / pagesize
39037602
A
355 alloc_count = zone.alloc_size / zone.elem_size
356 alloc_waste = zone.alloc_size % zone.elem_size
3e170ce0 357
39236c6e 358 marks = [
3e170ce0
A
359 ["collectable", "C"],
360 ["expandable", "X"],
361 ["noencrypt", "$"],
362 ["caller_acct", "@"],
363 ["exhaustible", "H"],
364 ["allows_foreign", "F"],
365 ["async_prio_refill", "R"],
366 ["no_callout", "O"],
367 ["zleak_on", "L"],
368 ["doing_alloc_without_vm_priv", "A"],
369 ["doing_alloc_with_vm_priv", "S"],
d9a64523
A
370 ["waiting", "W"],
371 ["cpu_cache_enabled", "E"]
39236c6e
A
372 ]
373 if kern.arch == 'x86_64':
374 marks.append(["gzalloc_exempt", "M"])
375 marks.append(["alignment_required", "N"])
5ba3f43e 376
39236c6e 377 markings=""
5ba3f43e
A
378 if not zone.__getattr__("zone_valid") :
379 markings+="I"
39236c6e
A
380 for mark in marks:
381 if zone.__getattr__(mark[0]) :
382 markings+=mark[1]
383 else:
384 markings+=" "
d9a64523
A
385 cache_elem_count = 0
386 if zone.__getattr__('cpu_cache_enabled') :
387 for i in range(0, kern.globals.machine_info.physical_cpu):
388 cache = zone.zcache[0].zcc_per_cpu_caches[i]
389 cache_elem_count += cache.current.zcc_magazine_index
390 cache_elem_count += cache.previous.zcc_magazine_index
391 if zone.zcache[0].zcc_depot_index != -1:
392 cache_elem_count += zone.zcache[0].zcc_depot_index * mag_capacity
393
cb323159
A
394 out_string += format_string.format(zone=zone, free_size=free_size, alloc_count=alloc_count,
395 alloc_pages=alloc_pages, alloc_waste=alloc_waste, cache_elem_count=cache_elem_count, markings=markings)
5ba3f43e 396
39236c6e
A
397 if zone.exhaustible :
398 out_string += "(max: {:d})".format(zone.max_size)
5ba3f43e 399
39236c6e
A
400 return out_string
401
cb323159
A
402@lldb_command('zprint', fancy=True)
403def Zprint(cmd_args=None, cmd_options={}, O=None):
39236c6e
A
404 """ Routine to print a summary listing of all the kernel zones
405 All columns are printed in decimal
406 Legend:
407 C - collectable
408 X - expandable
409 $ - not encrypted during hibernation
410 @ - allocs and frees are accounted to caller process for KPRVT
411 H - exhaustible
412 F - allows foreign memory (memory not allocated from zone_map)
413 M - gzalloc will avoid monitoring this zone
414 R - will be refilled when below low water mark
415 O - does not allow refill callout to fill zone on noblock allocation
416 N - zone requires alignment (avoids padding this zone for debugging)
3e170ce0
A
417 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
418 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
39236c6e 419 W - another thread is waiting for more memory
d9a64523 420 E - Per-cpu caching is enabled for this zone
39236c6e
A
421 L - zone is being monitored by zleaks
422 G - currently running GC
5ba3f43e 423 I - zone was destroyed and is no longer valid
39236c6e
A
424 """
425 global kern
cb323159
A
426 with O.table(GetZoneSummary.header):
427 for zval in kern.zones:
428 print GetZoneSummary(zval)
39236c6e
A
429
430@xnudebug_test('test_zprint')
431def TestZprint(kernel_target, config, lldb_obj, isConnected ):
432 """ Test the functionality of zprint command
5ba3f43e 433 returns
39236c6e 434 - False on failure
5ba3f43e 435 - True on success
39236c6e
A
436 """
437 if not isConnected:
438 print "Target is not connected. Cannot test memstats"
439 return False
440 res = lldb.SBCommandReturnObject()
441 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res)
442 result = res.GetOutput()
443 if len(result.split("\n")) > 2:
444 return True
5ba3f43e 445 else:
39236c6e
A
446 return False
447
448
449# EndMacro: zprint
450
451# Macro: showzfreelist
452
453def ShowZfreeListHeader(zone):
454 """ Helper routine to print a header for zone freelist.
455 (Since the freelist does not have a custom type, this is not defined as a Type Summary).
456 params:
457 zone:zone_t - Zone object to print header info
458 returns:
459 None
460 """
5ba3f43e 461
fe8ab488
A
462 scaled_factor = (unsigned(kern.globals.zp_factor) +
463 (unsigned(zone.elem_size) >> unsigned(kern.globals.zp_scale)))
464
39236c6e
A
465 out_str = ""
466 out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
467 out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
fe8ab488 468 zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, scaled_factor)
39236c6e
A
469 out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
470 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
471 print out_str
472
473def ShowZfreeListChain(zone, zfirst, zlimit):
474 """ Helper routine to print a zone free list chain
475 params:
476 zone: zone_t - Zone object
477 zfirst: void * - A pointer to the first element of the free list chain
478 zlimit: int - Limit for the number of elements to be printed by showzfreelist
479 returns:
480 None
481 """
482 current = Cast(zfirst, 'void *')
483 while ShowZfreeList.elts_found < zlimit:
484 ShowZfreeList.elts_found += 1
485 znext = dereference(Cast(current, 'vm_offset_t *'))
39037602
A
486 znext = (unsigned(znext) ^ unsigned(kern.globals.zp_nopoison_cookie))
487 znext = kern.GetValueFromAddress(znext, 'vm_offset_t *')
39236c6e
A
488 backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
489 backup_val = dereference(backup_ptr)
490 n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
491 p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
492 poison_str = ''
493 if p_unobfuscated == unsigned(znext):
494 poison_str = "P ({0: <d})".format(ShowZfreeList.elts_found - ShowZfreeList.last_poisoned)
495 ShowZfreeList.last_poisoned = ShowZfreeList.elts_found
496 else:
497 if n_unobfuscated != unsigned(znext):
498 poison_str = "INVALID"
499 print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
500 ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
501 if unsigned(znext) == 0:
502 break
503 current = Cast(znext, 'void *')
504
505@static_var('elts_found',0)
506@static_var('last_poisoned',0)
507@lldb_command('showzfreelist')
508def ShowZfreeList(cmd_args=None):
509 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
510 Usage: showzfreelist <zone> [iterations]
511
512 Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
513 """
514 if not cmd_args:
515 print ShowZfreeList.__doc__
516 return
517 ShowZfreeList.elts_found = 0
518 ShowZfreeList.last_poisoned = 0
519
520 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
521 zlimit = 50
522 if len(cmd_args) >= 2:
523 zlimit = ArgumentStringToInt(cmd_args[1])
524 ShowZfreeListHeader(zone)
525
39037602
A
526 if unsigned(zone.allows_foreign) == 1:
527 for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'):
39236c6e
A
528 if ShowZfreeList.elts_found == zlimit:
529 break
39037602 530 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
39236c6e
A
531 if unsigned(zfirst) != 0:
532 ShowZfreeListChain(zone, zfirst, zlimit)
39037602
A
533 for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'):
534 if ShowZfreeList.elts_found == zlimit:
535 break
536 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
537 if unsigned(zfirst) != 0:
538 ShowZfreeListChain(zone, zfirst, zlimit)
539 for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
540 if ShowZfreeList.elts_found == zlimit:
541 break
542 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
39236c6e
A
543 if unsigned(zfirst) != 0:
544 ShowZfreeListChain(zone, zfirst, zlimit)
5ba3f43e 545
39236c6e
A
546 if ShowZfreeList.elts_found == zlimit:
547 print "Stopped at {0: <d} elements!".format(zlimit)
548 else:
549 print "Found {0: <d} elements!".format(ShowZfreeList.elts_found)
550
551# EndMacro: showzfreelist
552
39037602
A
553# Macro: zstack_showzonesbeinglogged
554
555@lldb_command('zstack_showzonesbeinglogged')
556def ZstackShowZonesBeingLogged(cmd_args=None):
5ba3f43e 557 """ Show all zones which have BTLog enabled.
39037602 558 """
39037602
A
559 global kern
560 for zval in kern.zones:
561 if zval.zlog_btlog:
562 print "Zone: %s with its BTLog at: 0x%lx" % (zval.zone_name, zval.zlog_btlog)
563
564# EndMacro: zstack_showzonesbeinglogged
565
39236c6e
A
566# Macro: zstack
567
568@lldb_command('zstack')
569def Zstack(cmd_args=None):
39037602
A
570 """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>.
571 Usage: zstack <btlog addr> <index> [<count>]
39236c6e 572
39037602
A
573 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
574 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
39236c6e
A
575 """
576 if not cmd_args:
577 print Zstack.__doc__
578 return
579 if int(kern.globals.log_records) == 0:
580 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
581 return
39236c6e 582
39037602
A
583 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
584 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
585 btrecord_size = unsigned(btlog_ptr.btrecord_size)
586 btrecords = unsigned(btlog_ptr.btrecords)
587 btlog_size = unsigned(sizeof('struct btlog'))
588 depth = unsigned(btlog_ptr.btrecord_btdepth)
589 zstack_index = ArgumentStringToInt(cmd_args[1])
39236c6e 590 count = 1
39037602
A
591 if len(cmd_args) >= 3:
592 count = ArgumentStringToInt(cmd_args[2])
593
594 max_count = ((btrecords_total_size - btlog_size)/btrecord_size)
595
596 if (zstack_index + count) > max_count:
597 count = max_count - zstack_index
598
39236c6e 599 while count and (zstack_index != 0xffffff):
39037602
A
600 zstack_record_offset = zstack_index * btrecord_size
601 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
602 if int(zstack_record.ref_count)!=0:
603 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
604 zstack_index += 1
39236c6e
A
605 count -= 1
606
607# EndMacro : zstack
608
39037602 609# Macro: zstack_inorder
39236c6e 610
39037602
A
611@lldb_command('zstack_inorder')
612def ZstackInOrder(cmd_args=None):
613 """ Zone leak debugging: Print the stack traces starting from head to the tail.
614 Usage: zstack_inorder <btlog addr>
39236c6e 615 """
39037602
A
616 if not cmd_args:
617 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
39236c6e 618 return
39037602
A
619 if int(kern.globals.log_records) == 0:
620 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
39236c6e 621 return
39236c6e 622
39037602
A
623 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
624 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
625 btrecord_size = unsigned(btlog_ptr.btrecord_size)
626 btrecords = unsigned(btlog_ptr.btrecords)
627 btlog_size = unsigned(sizeof('struct btlog'))
628 depth = unsigned(btlog_ptr.btrecord_btdepth)
629 zstack_head = unsigned(btlog_ptr.head)
630 zstack_index = zstack_head
631 zstack_tail = unsigned(btlog_ptr.tail)
632 count = ((btrecords_total_size - btlog_size)/btrecord_size)
633
634 while count and (zstack_index != 0xffffff):
635 zstack_record_offset = zstack_index * btrecord_size
636 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
637 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
638 zstack_index = zstack_record.next
639 count -= 1
640
641# EndMacro : zstack_inorder
642
643# Macro: findoldest
644
645@lldb_command('findoldest')
646def FindOldest(cmd_args=None):
647 """
648 """
649 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
650 return
39236c6e
A
651# EndMacro : findoldest
652
39037602 653# Macro : zstack_findleak
39236c6e 654
39037602
A
655@lldb_command('zstack_findleak')
656def zstack_findleak(cmd_args=None):
657 """ Zone leak debugging: search the log and print the stack with the most active references
39236c6e 658 in the stack trace.
39037602 659 Usage: zstack_findleak <btlog address>
39236c6e 660
39037602
A
661 This is useful for verifying a suspected stack as being the source of
662 the leak.
39236c6e 663 """
39037602
A
664 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
665 btrecord_size = unsigned(btlog_ptr.btrecord_size)
666 btrecords = unsigned(btlog_ptr.btrecords)
667
668 cpcs_index = unsigned(btlog_ptr.head)
669 depth = unsigned(btlog_ptr.btrecord_btdepth)
670 highref = 0
671 highref_index = 0
672 highref_record = 0
39236c6e
A
673
674 while cpcs_index != 0xffffff:
39037602
A
675 cpcs_record_offset = cpcs_index * btrecord_size
676 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
677 if cpcs_record.ref_count > highref:
678 highref_record = cpcs_record
679 highref = cpcs_record.ref_count
680 highref_index = cpcs_index
39236c6e 681 cpcs_index = cpcs_record.next
39037602 682 ShowZStackRecord(highref_record, highref_index, depth, unsigned(btlog_ptr.active_element_count))
39236c6e 683
39037602 684# EndMacro: zstack_findleak
39236c6e
A
685
686# Macro: findelem
687
688@lldb_command('findelem')
689def FindElem(cmd_args=None):
39037602
A
690 """
691 """
692 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
693 return
694# EndMacro: findelem
695
696@lldb_command('zstack_findelem')
697def ZStackFindElem(cmd_args=None):
698 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
5ba3f43e 699 refer to the given zone element.
39037602 700 Usage: zstack_findelem <btlog addr> <elem addr>
39236c6e
A
701
702 When the kernel panics due to a corrupted zone element, get the
703 element address and use this command. This will show you the stack traces of all logged zalloc and
704 zfree operations which tells you who touched the element in the recent past. This also makes
705 double-frees readily apparent.
706 """
707 if not cmd_args:
39037602 708 print ZStackFindElem.__doc__
39236c6e 709 return
39037602
A
710 if int(kern.globals.log_records) == 0 or unsigned(kern.globals.corruption_debug_flag) == 0:
711 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
39236c6e 712 return
5ba3f43e 713
39037602
A
714 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
715 target_element = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
716
717 btrecord_size = unsigned(btlog_ptr.btrecord_size)
718 btrecords = unsigned(btlog_ptr.btrecords)
719 depth = unsigned(btlog_ptr.btrecord_btdepth)
39236c6e 720
39037602
A
721 prev_op = -1
722 scan_items = 0
723 hashelem = cast(btlog_ptr.elem_linkage_un.element_hash_queue.tqh_first, 'btlog_element_t *')
724 if (target_element >> 32) != 0:
725 target_element = target_element ^ 0xFFFFFFFFFFFFFFFF
726 else:
727 target_element = target_element ^ 0xFFFFFFFF
728 while hashelem != 0:
729 if unsigned(hashelem.elem) == target_element:
730 recindex = hashelem.recindex
731 recoffset = recindex * btrecord_size
732 record = kern.GetValueFromAddress(btrecords + recoffset, 'btlog_record_t *')
733 out_str = ('-' * 8)
734 if record.operation == 1:
735 out_str += "OP: ALLOC. "
736 else:
737 out_str += "OP: FREE. "
738 out_str += "Stack Index {0: <d} {1: <s}\n".format(recindex, ('-' * 8))
739 print out_str
740 print GetBtlogBacktrace(depth, record)
741 print " \n"
742 if int(record.operation) == prev_op:
39236c6e 743 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
39037602
A
744 return
745 prev_op = int(record.operation)
746 scan_items = 0
747 hashelem = cast(hashelem.element_hash_link.tqe_next, 'btlog_element_t *')
748 scan_items += 1
749 if scan_items % 100 == 0:
750 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items)
39236c6e 751
39037602 752# EndMacro: zstack_findelem
39236c6e 753
5ba3f43e
A
754@lldb_command('zstack_findtop', 'N:')
755def ShowZstackTop(cmd_args=None, cmd_options={}):
756 """ Zone leak debugging: search the log and print the stacks with the most active references
757 in the stack trace.
758
759 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
760 """
761
762 if not cmd_args:
763 raise ArgumentError('Missing required btlog address argument')
764
765 n = 5
766 if '-N' in cmd_options:
767 n = int(cmd_options['-N'])
768
769 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
770 btrecord_size = unsigned(btlog_ptr.btrecord_size)
771 btrecords = unsigned(btlog_ptr.btrecords)
772
773 cpcs_index = unsigned(btlog_ptr.head)
774 depth = unsigned(btlog_ptr.btrecord_btdepth)
775
776 records = []
777 while cpcs_index != 0xffffff:
778 cpcs_record_offset = cpcs_index * btrecord_size
779 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
780 cpcs_record.index = cpcs_index
781 records.append(cpcs_record)
782 cpcs_index = cpcs_record.next
783
784 recs = sorted(records, key=lambda x: x.ref_count, reverse=True)
785
786 for rec in recs[:n]:
787 ShowZStackRecord(rec, rec.index, depth, unsigned(btlog_ptr.active_element_count))
788
789# EndMacro: zstack_findtop
790
39236c6e
A
791# Macro: btlog_find
792
fe8ab488 793@lldb_command('btlog_find', "AS")
39236c6e 794def BtlogFind(cmd_args=None, cmd_options={}):
39236c6e 795 """
39037602
A
796 """
797 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
fe8ab488 798 return
39236c6e
A
799
800#EndMacro: btlog_find
801
802#Macro: showzalloc
803
804@lldb_command('showzalloc')
805def ShowZalloc(cmd_args=None):
806 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
807 Usage: showzalloc <index>
808 """
809 if not cmd_args:
810 print ShowZalloc.__doc__
811 return
812 if unsigned(kern.globals.zallocations) == 0:
813 print "zallocations array not initialized!"
814 return
815 zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])]
816 print zallocation
817 ShowZTrace([str(int(zallocation.za_trace_index))])
818
819#EndMacro: showzalloc
820
821#Macro: showztrace
822
823@lldb_command('showztrace')
824def ShowZTrace(cmd_args=None):
825 """ Prints the backtrace from the ztraces array at index
826 Usage: showztrace <trace index>
827 """
828 if not cmd_args:
829 print ShowZTrace.__doc__
830 return
831 if unsigned(kern.globals.ztraces) == 0:
832 print "ztraces array not initialized!"
833 return
834 ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])]
835 print ztrace_addr
836 ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth)
837
838#EndMacro: showztrace
839
840#Macro: showztraceaddr
841
842@lldb_command('showztraceaddr')
843def ShowZTraceAddr(cmd_args=None):
844 """ Prints the struct ztrace passed in.
845 Usage: showztraceaddr <trace address>
846 """
847 if not cmd_args:
848 print ShowZTraceAddr.__doc__
849 return
850 ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *')
851 print dereference(ztrace_ptr)
852 ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth)
853
854#EndMacro: showztraceaddr
855
856#Macro: showzstacktrace
857
858@lldb_command('showzstacktrace')
859def ShowZstackTrace(cmd_args=None):
860 """ Routine to print a stacktrace stored by OSBacktrace.
861 Usage: showzstacktrace <saved stacktrace> [size]
862
863 size is optional, defaults to 15.
864 """
865 if not cmd_args:
866 print ShowZstackTrace.__doc__
867 return
868 void_ptr_type = gettype('void *')
869 void_double_ptr_type = void_ptr_type.GetPointerType()
870 trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type)
871 trace_size = 15
872 if len(cmd_args) >= 2:
873 trace_size = ArgumentStringToInt(cmd_args[1])
874 ShowZstackTraceHelper(trace, trace_size)
5ba3f43e 875
39236c6e
A
876#EndMacro: showzstacktrace
877
878def ShowZstackTraceHelper(stack, depth):
879 """ Helper routine for printing a zstack.
880 params:
881 stack: void *[] - An array of pointers representing the Zstack
5ba3f43e 882 depth: int - The depth of the ztrace stack
39236c6e
A
883 returns:
884 None
885 """
886 trace_current = 0
887 while trace_current < depth:
888 trace_addr = stack[trace_current]
889 symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr))
890 if symbol_arr:
891 symbol_str = str(symbol_arr[0].addr)
892 else:
893 symbol_str = ''
894 print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str)
895 trace_current += 1
896
897#Macro: showtopztrace
898
899@lldb_command('showtopztrace')
900def ShowTopZtrace(cmd_args=None):
5ba3f43e 901 """ Shows the ztrace with the biggest size.
39236c6e
A
902 (According to top_ztrace, not by iterating through the hash table)
903 """
904 top_trace = kern.globals.top_ztrace
905 print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace'))
906 print dereference(top_trace)
907 ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth)
908
909#EndMacro: showtopztrace
910
911#Macro: showzallocs
912
913@lldb_command('showzallocs')
914def ShowZallocs(cmd_args=None):
915 """ Prints all allocations in the zallocations table
916 """
917 if unsigned(kern.globals.zallocations) == 0:
918 print "zallocations array not initialized!"
919 return
5ba3f43e 920 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
39236c6e
A
921 current_index = 0
922 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
923 allocation_count = 0
924 while current_index < max_zallocation:
925 current_zalloc = kern.globals.zallocations[current_index]
926 if int(current_zalloc.za_element) != 0:
927 print '{0: <5d} {1: <#018x} {2: <5d} {3: <15d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_trace_index, unsigned(current_zalloc.za_size))
928 allocation_count += 1
929 current_index += 1
930 print 'Total Allocations: {0: <d}'.format(allocation_count)
931
932#EndMacro: showzallocs
933
934#Macro: showzallocsfortrace
935
936@lldb_command('showzallocsfortrace')
937def ShowZallocsForTrace(cmd_args=None):
938 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
939 Usage: showzallocsfortrace <trace index>
940 """
941 if not cmd_args:
942 print ShowZallocsForTrace.__doc__
943 return
5ba3f43e 944 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
39236c6e
A
945 target_index = ArgumentStringToInt(cmd_args[0])
946 current_index = 0
947 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
948 allocation_count = 0
949 while current_index < max_zallocation:
950 current_zalloc = kern.globals.zallocations[current_index]
951 if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)):
952 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size)
953 allocation_count += 1
954 current_index += 1
955 print 'Total Allocations: {0: <d}'.format(allocation_count)
956
957#EndMacro: showzallocsfortrace
958
959#Macro: showztraces
960
961@lldb_command('showztraces')
962def ShowZTraces(cmd_args=None):
963 """ Prints all traces with size > 0
964 """
965 ShowZTracesAbove([0])
966
967#EndMacro: showztraces
968
969#Macro: showztracesabove
970
971@lldb_command('showztracesabove')
972def ShowZTracesAbove(cmd_args=None):
973 """ Prints all traces with size greater than X
974 Usage: showztracesabove <size>
975 """
976 if not cmd_args:
977 print ShowZTracesAbove.__doc__
978 return
979 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
980 current_index = 0
981 ztrace_count = 0
982 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
983 while current_index < max_ztrace:
984 ztrace_current = kern.globals.ztraces[current_index]
985 if ztrace_current.zt_size > unsigned(cmd_args[0]):
986 print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size))
987 ztrace_count += 1
988 current_index += 1
989 print 'Total traces: {0: <d}'.format(ztrace_count)
990
991#EndMacro: showztracesabove
992
993#Macro: showztracehistogram
994
995@lldb_command('showztracehistogram')
996def ShowZtraceHistogram(cmd_args=None):
997 """ Prints the histogram of the ztrace table
998 """
999 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
1000 current_index = 0
1001 ztrace_count = 0
1002 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
1003 while current_index < max_ztrace:
1004 ztrace_current = kern.globals.ztraces[current_index]
1005 if ztrace_current.zt_hit_count != 0:
1006 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions)
1007 ztrace_count += 1
1008 current_index += 1
1009 print 'Total traces: {0: <d}'.format(ztrace_count)
1010
1011#EndMacro: showztracehistogram
1012
1013#Macro: showzallochistogram
1014
1015@lldb_command('showzallochistogram')
1016def ShowZallocHistogram(cmd_args=None):
1017 """ Prints the histogram for the zalloc table
1018 """
1019 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
1020 current_index = 0
1021 zallocation_count = 0
1022 max_ztrace = unsigned(kern.globals.zleak_alloc_buckets)
1023 while current_index < max_ztrace:
1024 zallocation_current = kern.globals.zallocations[current_index]
1025 if zallocation_current.za_hit_count != 0:
1026 print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count)
1027 zallocation_count += 1
1028 current_index += 1
1029 print 'Total Allocations: {0: <d}'.format(zallocation_count)
1030
1031#EndMacro: showzallochistogram
1032
1033#Macro: showzstats
1034
1035@lldb_command('showzstats')
1036def ShowZstats(cmd_args=None):
1037 """ Prints the zone leak detection stats
1038 """
1039 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions))
1040 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites))
1041 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded))
1042
1043#EndMacro: showzstats
1044
fe8ab488
A
1045def GetBtlogBacktrace(depth, zstack_record):
1046 """ Helper routine for getting a BT Log record backtrace stack.
39236c6e
A
1047 params:
1048 depth:int - The depth of the zstack record
1049 zstack_record:btlog_record_t * - A BTLog record
1050 returns:
fe8ab488 1051 str - string with backtrace in it.
39236c6e
A
1052 """
1053 out_str = ''
1054 frame = 0
1055 if not zstack_record:
fe8ab488 1056 return "Zstack record none!"
5ba3f43e 1057
39236c6e
A
1058 depth_val = unsigned(depth)
1059 while frame < depth_val:
1060 frame_pc = zstack_record.bt[frame]
1061 if not frame_pc or int(frame_pc) == 0:
1062 break
1063 symbol_arr = kern.SymbolicateFromAddress(frame_pc)
1064 if symbol_arr:
1065 symbol_str = str(symbol_arr[0].addr)
1066 else:
1067 symbol_str = ''
1068 out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
1069 frame += 1
fe8ab488 1070 return out_str
39236c6e 1071
39037602 1072def ShowZStackRecord(zstack_record, zstack_index, btrecord_btdepth, elements_count):
39236c6e
A
1073 """ Helper routine for printing a single zstack record
1074 params:
1075 zstack_record:btlog_record_t * - A BTLog record
1076 zstack_index:int - Index for the record in the BTLog table
1077 returns:
1078 None
1079 """
1080 out_str = ('-' * 8)
1081 if zstack_record.operation == 1:
39037602 1082 out_str += "ALLOC. "
39236c6e 1083 else:
39037602
A
1084 out_str += "FREE. "
1085 out_str += "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index, zstack_record.ref_count, elements_count, ('-' * 8))
39236c6e 1086 print out_str
39037602
A
1087 print GetBtlogBacktrace(btrecord_btdepth, zstack_record)
1088 print " \n"
39236c6e
A
1089
1090# Macro: showioalloc
1091
1092@lldb_command('showioalloc')
1093def ShowIOAllocations(cmd_args=None):
1094 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
1095 Routine to display a summary of memory accounting allocated by IOKit allocators.
1096 """
1097 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024))
1098 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
1099 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
1100 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
5ba3f43e
A
1101
1102
1103# EndMacro: showioalloc
39236c6e
A
1104
1105
3e170ce0
A
1106# Macro: showselectmem
1107@lldb_command('showselectmem', "S:")
1108def ShowSelectMem(cmd_args=None, cmd_options={}):
1109 """ Show memory cached by threads on calls to select.
1110
1111 usage: showselectmem [-v]
1112 -v : print each thread's memory
1113 (one line per thread with non-zero select memory)
1114 -S {addr} : Find the thread whose thread-local select set
1115 matches the given address
1116 """
1117 verbose = False
1118 opt_wqs = 0
1119 if config['verbosity'] > vHUMAN:
1120 verbose = True
1121 if "-S" in cmd_options:
1122 opt_wqs = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *'))
1123 if opt_wqs == 0:
1124 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options["-S"]))
1125 selmem = 0
1126 if verbose:
1127 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1128 for t in kern.tasks:
1129 for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
1130 uth = Cast(th.uthread, 'uthread *');
1131 wqs = 0
1132 if hasattr(uth, 'uu_allocsize'): # old style
1133 thmem = uth.uu_allocsize
1134 wqs = uth.uu_wqset
1135 elif hasattr(uth, 'uu_wqstate_sz'): # new style
1136 thmem = uth.uu_wqstate_sz
1137 wqs = uth.uu_wqset
1138 else:
1139 print "What kind of uthread is this?!"
1140 return
1141 if opt_wqs and opt_wqs == unsigned(wqs):
1142 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs, unsigned(th), unsigned(th.thread_id))
1143 if verbose and thmem > 0:
1144 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t), unsigned(th.thread_id), thmem)
1145 selmem += thmem
1146 print '-'*40
1147 print "Total: {:d} bytes ({:d} kbytes)".format(selmem, selmem/1024)
1148# Endmacro: showselectmem
5ba3f43e
A
1149
1150
39236c6e 1151# Macro: showtaskvme
fe8ab488
A
1152@lldb_command('showtaskvme', "PS")
1153def ShowTaskVmeHelper(cmd_args=None, cmd_options={}):
39236c6e
A
1154 """ Display a summary list of the specified vm_map's entries
1155 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
3e170ce0
A
1156 Use -S flag to show VM object shadow chains
1157 Use -P flag to show pager info (mapped file, compressed pages, ...)
39236c6e 1158 """
fe8ab488
A
1159 show_pager_info = False
1160 show_all_shadows = False
1161 if "-P" in cmd_options:
1162 show_pager_info = True
1163 if "-S" in cmd_options:
1164 show_all_shadows = True
39236c6e 1165 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
fe8ab488 1166 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
39236c6e 1167
fe8ab488
A
1168@lldb_command('showallvme', "PS")
1169def ShowAllVME(cmd_args=None, cmd_options={}):
39236c6e 1170 """ Routine to print a summary listing of all the vm map entries
fe8ab488
A
1171 Go Through each task in system and show the vm memory regions
1172 Use -S flag to show VM object shadow chains
1173 Use -P flag to show pager info (mapped file, compressed pages, ...)
1174 """
1175 show_pager_info = False
1176 show_all_shadows = False
1177 if "-P" in cmd_options:
1178 show_pager_info = True
1179 if "-S" in cmd_options:
1180 show_all_shadows = True
39236c6e 1181 for task in kern.tasks:
fe8ab488 1182 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
39236c6e
A
1183
1184@lldb_command('showallvm')
1185def ShowAllVM(cmd_args=None):
1186 """ Routine to print a summary listing of all the vm maps
1187 """
1188 for task in kern.tasks:
1189 print GetTaskSummary.header + ' ' + GetProcSummary.header
1190 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1191 print GetVMMapSummary.header
1192 print GetVMMapSummary(task.map)
1193
1194@lldb_command("showtaskvm")
1195def ShowTaskVM(cmd_args=None):
1196 """ Display info about the specified task's vm_map
1197 syntax: (lldb) showtaskvm <task_ptr>
1198 """
1199 if not cmd_args:
1200 print ShowTaskVM.__doc__
1201 return False
1202 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
1203 if not task:
1204 print "Unknown arguments."
1205 return False
1206 print GetTaskSummary.header + ' ' + GetProcSummary.header
1207 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1208 print GetVMMapSummary.header
1209 print GetVMMapSummary(task.map)
1210 return True
1211
1212@lldb_command('showallvmstats')
1213def ShowAllVMStats(cmd_args=None):
1214 """ Print a summary of vm statistics in a table format
1215 """
3e170ce0 1216 page_size = kern.globals.page_size
39236c6e
A
1217 vmstats = lambda:None
1218 vmstats.wired_count = 0
1219 vmstats.resident_count = 0
1220 vmstats.resident_max = 0
1221 vmstats.internal = 0
1222 vmstats.external = 0
1223 vmstats.reusable = 0
1224 vmstats.compressed = 0
1225 vmstats.compressed_peak = 0
1226 vmstats.compressed_lifetime = 0
1227 vmstats.error = ''
1228
d9a64523
A
1229 hdr_format = "{:>6s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<20s} {:1s}"
1230 print hdr_format.format('#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', 'pid', 'command', '')
1231 print hdr_format.format('', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '', '', '')
1232 entry_format = "{m.hdr.nentries: >6d} {s.wired_count: >10d} {vsize: >10d} {s.resident_count: >10d} {s.new_resident_count: >10d} {s.resident_max: >10d} {s.internal: >10d} {s.external: >10d} {s.reusable: >10d} {s.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {p.p_pid: >10d} {p.p_comm: <20s} {s.error}"
39236c6e
A
1233
1234 for task in kern.tasks:
1235 proc = Cast(task.bsd_info, 'proc *')
1236 vmmap = Cast(task.map, '_vm_map *')
1237 vmstats.error = ''
1238 vmstats.wired_count = vmmap.pmap.stats.wired_count;
1239 vmstats.resident_count = unsigned(vmmap.pmap.stats.resident_count);
1240 vmstats.resident_max = vmmap.pmap.stats.resident_max;
1241 vmstats.internal = unsigned(vmmap.pmap.stats.internal);
1242 vmstats.external = unsigned(vmmap.pmap.stats.external);
1243 vmstats.reusable = unsigned(vmmap.pmap.stats.reusable);
1244 vmstats.compressed = unsigned(vmmap.pmap.stats.compressed);
1245 vmstats.compressed_peak = unsigned(vmmap.pmap.stats.compressed_peak);
1246 vmstats.compressed_lifetime = unsigned(vmmap.pmap.stats.compressed_lifetime);
1247 vmstats.new_resident_count = vmstats.internal + vmstats.external
1248
1249 if vmstats.internal < 0:
1250 vmstats.error += '*'
1251 if vmstats.external < 0:
1252 vmstats.error += '*'
1253 if vmstats.reusable < 0:
1254 vmstats.error += '*'
1255 if vmstats.compressed < 0:
1256 vmstats.error += '*'
1257 if vmstats.compressed_peak < 0:
1258 vmstats.error += '*'
1259 if vmstats.compressed_lifetime < 0:
1260 vmstats.error += '*'
1261 if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
1262 vmstats.error += '*'
1263
3e170ce0 1264 print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) / page_size), t=task, s=vmstats)
5ba3f43e 1265
39236c6e 1266
fe8ab488 1267def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
39236c6e 1268 """ Routine to print out a summary listing of all the entries in a vm_map
5ba3f43e 1269 params:
39236c6e
A
1270 task - core.value : a object of type 'task *'
1271 returns:
1272 None
1273 """
1274 print "vm_map entries for task " + hex(task)
1275 print GetTaskSummary.header
1276 print GetTaskSummary(task)
1277 if not task.map:
1278 print "Task {0: <#020x} has map = 0x0"
1279 return None
1280 print GetVMMapSummary.header
1281 print GetVMMapSummary(task.map)
1282 vme_list_head = task.map.hdr.links
1283 vme_ptr_type = GetType('vm_map_entry *')
1284 print GetVMEntrySummary.header
1285 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
fe8ab488 1286 print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
39236c6e
A
1287 return None
1288
1289@lldb_command("showmap")
1290def ShowMap(cmd_args=None):
1291 """ Routine to print out info about the specified vm_map
1292 usage: showmap <vm_map>
1293 """
1294 if cmd_args == None or len(cmd_args) < 1:
1295 print "Invalid argument.", ShowMap.__doc__
1296 return
1297 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1298 print GetVMMapSummary.header
1299 print GetVMMapSummary(map_val)
1300
1301@lldb_command("showmapvme")
1302def ShowMapVME(cmd_args=None):
1303 """Routine to print out info about the specified vm_map and its vm entries
1304 usage: showmapvme <vm_map>
1305 """
1306 if cmd_args == None or len(cmd_args) < 1:
d9a64523 1307 print "Invalid argument.", ShowMapVME.__doc__
39236c6e
A
1308 return
1309 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1310 print GetVMMapSummary.header
1311 print GetVMMapSummary(map_val)
1312 vme_list_head = map_val.hdr.links
1313 vme_ptr_type = GetType('vm_map_entry *')
1314 print GetVMEntrySummary.header
1315 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1316 print GetVMEntrySummary(vme)
1317 return None
1318
1319@lldb_type_summary(['_vm_map *', 'vm_map_t'])
1320@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free"))
1321def GetVMMapSummary(vmmap):
1322 """ Display interesting bits from vm_map struct """
1323 out_string = ""
1324 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
1325 vm_size = uint64_t(vmmap.size).value
1326 resident_pages = 0
1327 if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
4bd07ac2 1328 first_free = 0
d9a64523 1329 if int(vmmap.holelistenabled) == 0: first_free = vmmap.f_s._first_free
4bd07ac2 1330 out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, first_free)
39236c6e
A
1331 return out_string
1332
1333@lldb_type_summary(['vm_map_entry'])
1334@header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1335def GetVMEntrySummary(vme):
1336 """ Display vm entry specific information. """
3e170ce0 1337 page_size = kern.globals.page_size
39236c6e
A
1338 out_string = ""
1339 format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1340 vme_protection = int(vme.protection)
1341 vme_max_protection = int(vme.max_protection)
1342 vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
5ba3f43e 1343 if int(vme.is_sub_map) != 0 :
39236c6e
A
1344 vme_extra_info_str +="s"
1345 elif int(vme.needs_copy) != 0 :
1346 vme_extra_info_str +="n"
3e170ce0
A
1347 num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) / page_size
1348 out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.vme_object.vmo_object, vme.vme_offset)
39236c6e
A
1349 return out_string
1350
1351# EndMacro: showtaskvme
1352@lldb_command('showmapwired')
1353def ShowMapWired(cmd_args=None):
1354 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1355 """
1356 if cmd_args == None or len(cmd_args) < 1:
1357 print "Invalid argument", ShowMapWired.__doc__
1358 return
1359 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
39037602 1360
39236c6e
A
1361
1362@lldb_type_summary(['kmod_info_t *'])
39037602 1363@header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name'))
39236c6e 1364def GetKextSummary(kmod):
5ba3f43e 1365 """ returns a string representation of kext information
39236c6e
A
1366 """
1367 out_string = ""
39037602
A
1368 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1369 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod.address), unsigned(kmod.size))
1370 text_segment = macho.get_text_segment(segments)
1371 if not text_segment:
1372 text_segment = segments[0]
1373 out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, text_segment.vmaddr, text_segment.vmsize, kmod.version, kmod.name)
39236c6e
A
1374 return out_string
1375
1376@lldb_type_summary(['uuid_t'])
5ba3f43e 1377@header("")
39236c6e
A
1378def GetUUIDSummary(uuid):
1379 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1380 """
1381 arr = Cast(addressof(uuid), 'uint8_t *')
1382 data = []
1383 for i in range(16):
1384 data.append(int(arr[i]))
1385 return "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=data)
1386
1387@lldb_command('showallkmods')
1388def ShowAllKexts(cmd_args=None):
1389 """Display a summary listing of all loaded kexts (alias: showallkmods)
1390 """
1391 kmod_val = kern.globals.kmod
39037602 1392 kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN))
39236c6e 1393 print "{: <36s} ".format("UUID") + GetKextSummary.header
39236c6e
A
1394 for kval in IterateLinkedList(kmod_val, 'next'):
1395 uuid = "........-....-....-....-............"
1396 kaddr = unsigned(kval.address)
39037602 1397 found_kext_summary = None
39236c6e 1398 for l in kextuuidinfo :
39037602 1399 if kaddr == int(l[3],16):
39236c6e 1400 uuid = l[0]
39037602 1401 found_kext_summary = l
39236c6e 1402 break
39037602
A
1403 if found_kext_summary:
1404 _ksummary = GetKextSummary(found_kext_summary[7])
1405 else:
1406 _ksummary = GetKextSummary(kval)
1407 print uuid + " " + _ksummary
39236c6e 1408
39037602
A
1409def GetKmodWithAddr(addr):
1410 """ Go through kmod list and find one with begin_addr as addr
1411 returns: None if not found. else a cvalue of type kmod
1412 """
1413 kmod_val = kern.globals.kmod
1414 for kval in IterateLinkedList(kmod_val, 'next'):
1415 if addr == unsigned(kval.address):
1416 return kval
1417 return None
1418
1419def GetAllSegmentsAndSectionsFromDataInMemory(address, size):
1420 """ reads memory at address and parses mach_header to get segment and section information
1421 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
1422 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
1423 if TEXT segment is not found a dummy segment & section with address, size is returned.
1424 """
1425 cache_hash = "kern.kexts.segments.{}.{}".format(address, size)
1426 cached_result = caching.GetDynamicCacheData(cache_hash,())
1427 if cached_result:
1428 return cached_result
1429
1430 defval = macho.MachOSegment('__TEXT', address, size, 0, size)
1431 if address == 0 or size == 0:
1432 return ([defval], [defval])
1433
5ba3f43e 1434 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
39037602 1435 # until we have separate version. we will pay penalty only on arm64 devices
5ba3f43e 1436 if not kern.arch.startswith('arm64'):
39037602
A
1437 return ([defval], [defval])
1438
1439 restrict_size_to_read = 1536
1440 machoObject = None
1441 while machoObject is None:
1442 err = lldb.SBError()
1443 size_to_read = min(size, restrict_size_to_read)
1444 data = LazyTarget.GetProcess().ReadMemory(address, size_to_read, err)
1445 if not err.Success():
1446 print "Failed to read memory at {} and size {}".format(address, size_to_read)
1447 return ([defval], [defval])
1448 try:
1449 m = macho.MemMacho(data, len(data))
1450 machoObject = m
1451 except Exception as e:
1452 if str(e.message).find('unpack requires a string argument') >= 0:
1453 # this may be due to short read of memory. Lets do double read size.
1454 restrict_size_to_read *= 2
1455 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read))
1456 continue
1457 else:
1458 print "Failed to read MachO for address {} errormessage: {}".format(address, e.message)
1459 return ([defval], [defval])
1460 # end of while loop. We have machoObject defined
1461 segments = machoObject.get_segments_with_name('')
1462 sections = machoObject.get_sections_with_name('')
1463 rval = (segments, sections)
1464 caching.SaveDynamicCacheData(cache_hash, rval)
1465 return rval
1466
1467def GetKextLoadInformation(addr=0, show_progress=False):
39236c6e
A
1468 """ Extract the kext uuid and load address information from the kernel data structure.
1469 params:
1470 addr - int - optional integer that is the address to search for.
39037602
A
1471 returns:
1472 [] - array with each entry of format
1473 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
1474 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
39236c6e 1475 """
39037602 1476 cached_result = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
5ba3f43e 1477 ## if specific addr is provided then ignore caching
39037602
A
1478 if cached_result and not addr:
1479 return cached_result
1480
1481 # because of <rdar://problem/12683084>, we can't find summaries directly
39236c6e
A
1482 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1483 baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
1484 summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
1485 total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries)
1486 kext_version = int(kern.globals.gLoadedKextSummaries.version)
1487 entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4
1488 if kext_version >= 2 :
1489 entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
1490 retval = []
1491 for i in range(total_summaries):
39037602
A
1492 if show_progress:
1493 print "progress: {}/{}".format(i, total_summaries)
39236c6e
A
1494 tmpaddress = unsigned(summaries_begin) + (i * entry_size)
1495 current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
39037602
A
1496 # code to extract macho information
1497 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext.address), unsigned(current_kext.size))
1498 seginfo = macho.get_text_segment(segments)
1499 if not seginfo:
1500 seginfo = segments[0]
1501 kmod_obj = GetKmodWithAddr(unsigned(current_kext.address))
39236c6e 1502 if addr != 0 :
39037602
A
1503 if addr == unsigned(current_kext.address) or addr == seginfo.vmaddr:
1504 return [(GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)]
1505 retval.append((GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj))
1506
1507 if not addr:
1508 caching.SaveDynamicCacheData("kern.kexts.loadinformation", retval)
39236c6e
A
1509 return retval
1510
1511lldb_alias('showallkexts', 'showallkmods')
1512
1513def GetOSKextVersion(version_num):
1514 """ returns a string of format 1.2.3x from the version_num
1515 params: version_num - int
39037602 1516 return: str
39236c6e
A
1517 """
1518 if version_num == -1 :
1519 return "invalid"
1520 (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
1521 version = version_num
5ba3f43e 1522
39236c6e
A
1523 vers_major = version / MAJ_MULT
1524 version = version - (vers_major * MAJ_MULT)
5ba3f43e 1525
39236c6e
A
1526 vers_minor = version / MIN_MULT
1527 version = version - (vers_minor * MIN_MULT)
5ba3f43e 1528
39236c6e
A
1529 vers_revision = version / REV_MULT
1530 version = version - (vers_revision * REV_MULT)
5ba3f43e 1531
39236c6e
A
1532 vers_stage = version / STAGE_MULT
1533 version = version - (vers_stage * STAGE_MULT)
5ba3f43e
A
1534
1535 vers_stage_level = version
1536
39236c6e
A
1537 out_str = "%d.%d" % (vers_major, vers_minor)
1538 if vers_revision > 0: out_str += ".%d" % vers_revision
1539 if vers_stage == 1 : out_str += "d%d" % vers_stage_level
1540 if vers_stage == 3 : out_str += "a%d" % vers_stage_level
1541 if vers_stage == 5 : out_str += "b%d" % vers_stage_level
1542 if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
5ba3f43e 1543
39236c6e
A
1544 return out_str
1545
1546@lldb_command('showallknownkmods')
1547def ShowAllKnownKexts(cmd_args=None):
1548 """ Display a summary listing of all kexts known in the system.
1549 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1550 """
1551 kext_count = int(kern.globals.sKextsByID.count)
1552 index = 0
1553 kext_dictionary = kern.globals.sKextsByID.dictionary
1554 print "%d kexts in sKextsByID:" % kext_count
1555 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1556 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
39037602 1557
39236c6e
A
1558 while index < kext_count:
1559 kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
1560 kext_name = str(kext_dict.key.string)
1561 osk = Cast(kext_dict.value, 'OSKext *')
1562 if int(osk.flags.loaded) :
1563 load_addr = "{0: <#020x}".format(osk.kmod_info)
1564 id = "{0: >5d}".format(osk.loadTag)
1565 else:
1566 load_addr = "------"
1567 id = "--"
1568 version_num = unsigned(osk.version)
1569 version = GetOSKextVersion(version_num)
1570 print format_string.format(osk, load_addr, id, version, kext_name)
1571 index += 1
39037602 1572
39236c6e
A
1573 return
1574
5ba3f43e
A
1575def FindKmodNameForAddr(addr):
1576 """ Given an address, return the name of the kext containing that address
1577 """
1578 addr = unsigned(addr)
1579 all_kexts_info = GetKextLoadInformation()
1580 for kinfo in all_kexts_info:
1581 segment = macho.get_segment_with_addr(kinfo[4], addr)
1582 if segment:
1583 return kinfo[7].name
1584 return None
1585
1586
1587@lldb_command('addkextaddr')
1588def AddKextAddr(cmd_args=[]):
1589 """ Given an address, load the kext which contains that address
1590 Syntax: (lldb) addkextaddr <addr>
1591 """
1592 if len(cmd_args) < 1:
1593 raise ArgumentError("Insufficient arguments")
1594
1595 addr = ArgumentStringToInt(cmd_args[0])
1596 all_kexts_info = GetKextLoadInformation()
d9a64523 1597 kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
5ba3f43e
A
1598 found_kinfo = None
1599 found_segment = None
1600 for kinfo in all_kexts_info:
1601 segment = macho.get_segment_with_addr(kinfo[4], addr)
1602 if segment:
1603 print GetKextSummary.header
1604 print GetKextSummary(kinfo[7]) + " segment: {} offset = {:#0x}".format(segment.name, (addr - segment.vmaddr))
1605 cur_uuid = kinfo[0].lower()
d9a64523
A
1606 if (kernel_uuid == cur_uuid):
1607 print "(builtin)"
5ba3f43e 1608 else:
d9a64523
A
1609 print "Fetching dSYM for %s" % cur_uuid
1610 info = dsymForUUID(cur_uuid)
1611 if info and 'DBGSymbolRichExecutable' in info:
1612 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1613 addDSYM(cur_uuid, info)
1614 loadDSYM(cur_uuid, int(kinfo[1],16), kinfo[4])
1615 else:
1616 print "Failed to get symbol info for %s" % cur_uuid
5ba3f43e
A
1617 return
1618
1619
39236c6e
A
1620@lldb_command('showkmodaddr')
1621def ShowKmodAddr(cmd_args=[]):
39037602 1622 """ Given an address, print the offset and name for the kmod containing it
39236c6e
A
1623 Syntax: (lldb) showkmodaddr <addr>
1624 """
1625 if len(cmd_args) < 1:
1626 raise ArgumentError("Insufficient arguments")
1627
1628 addr = ArgumentStringToInt(cmd_args[0])
39037602
A
1629 all_kexts_info = GetKextLoadInformation()
1630 found_kinfo = None
1631 found_segment = None
1632 for kinfo in all_kexts_info:
1633 s = macho.get_segment_with_addr(kinfo[4], addr)
1634 if s:
1635 found_segment = s
1636 found_kinfo = kinfo
1637 break
1638 if found_kinfo:
1639 print GetKextSummary.header
1640 print GetKextSummary(found_kinfo[7]) + " segment: {} offset = {:#0x}".format(found_segment.name, (addr - found_segment.vmaddr))
1641 return True
39236c6e
A
1642 return False
1643
39037602 1644
fe8ab488 1645@lldb_command('addkext','AF:N:')
39236c6e
A
1646def AddKextSyms(cmd_args=[], cmd_options={}):
1647 """ Add kext symbols into lldb.
1648 This command finds symbols for a uuid and load the required executable
39037602 1649 Usage:
39236c6e
A
1650 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1651 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1652 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
fe8ab488 1653 addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
39037602 1654 addkext all : Will load all the kext symbols - SLOW
39236c6e 1655 """
39037602 1656
39236c6e
A
1657
1658 if "-F" in cmd_options:
1659 exec_path = cmd_options["-F"]
1660 exec_full_path = ResolveFSPath(exec_path)
1661 if not os.path.exists(exec_full_path):
1662 raise ArgumentError("Unable to resolve {:s}".format(exec_path))
1663
1664 if not os.path.isfile(exec_full_path):
1665 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1666\nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1667\nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
39236c6e
A
1668
1669 slide_value = None
5ba3f43e 1670 sections = None
39236c6e
A
1671 if cmd_args:
1672 slide_value = cmd_args[0]
1673 debuglog("loading slide value from user input %s" % cmd_args[0])
1674
1675 filespec = lldb.SBFileSpec(exec_full_path, False)
1676 print "target modules add %s" % exec_full_path
1677 print lldb_run_command("target modules add %s" % exec_full_path)
1678 loaded_module = LazyTarget.GetTarget().FindModule(filespec)
1679 if loaded_module.IsValid():
1680 uuid_str = loaded_module.GetUUIDString()
1681 debuglog("added module %s with uuid %s" % (exec_full_path, uuid_str))
1682 if slide_value is None:
1683 all_kexts_info = GetKextLoadInformation()
1684 for k in all_kexts_info:
1685 debuglog(k[0])
1686 if k[0].lower() == uuid_str.lower():
1687 slide_value = k[1]
5ba3f43e 1688 sections = k[4]
39236c6e 1689 debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
39236c6e
A
1690 if slide_value is None:
1691 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
5ba3f43e
A
1692
1693 if not sections:
1694 cmd_str = "target modules load --file %s --slide %s" % ( exec_full_path, str(slide_value))
1695 debuglog(cmd_str)
1696 else:
1697 cmd_str = "target modules load --file {} ".format(exec_full_path)
1698 sections_str = ""
1699 for s in sections:
1700 sections_str += " {} {:#0x} ".format(s.name, s.vmaddr)
1701 cmd_str += sections_str
1702 debuglog(cmd_str)
1703
1704 lldb.debugger.HandleCommand(cmd_str)
1705
39236c6e
A
1706 kern.symbolicator = None
1707 return True
1708
1709 all_kexts_info = GetKextLoadInformation()
d9a64523 1710 kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
39037602 1711
39236c6e
A
1712 if "-N" in cmd_options:
1713 kext_name = cmd_options["-N"]
1714 kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
fe8ab488 1715 if len(kext_name_matches) != 1 and "-A" not in cmd_options:
39236c6e
A
1716 print "Ambiguous match for name: {:s}".format(kext_name)
1717 if len(kext_name_matches) > 0:
1718 print "Options are:\n\t" + "\n\t".join(kext_name_matches)
1719 return
1720 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
fe8ab488
A
1721 for cur_knm in kext_name_matches:
1722 for x in all_kexts_info:
1723 if cur_knm == x[2]:
1724 cur_uuid = x[0].lower()
d9a64523
A
1725 if (kernel_uuid == cur_uuid):
1726 print "(builtin)"
fe8ab488 1727 else:
d9a64523
A
1728 print "Fetching dSYM for {:s}".format(cur_uuid)
1729 info = dsymForUUID(cur_uuid)
1730 if info and 'DBGSymbolRichExecutable' in info:
1731 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
1732 addDSYM(cur_uuid, info)
1733 loadDSYM(cur_uuid, int(x[1],16), x[4])
1734 else:
1735 print "Failed to get symbol info for {:s}".format(cur_uuid)
fe8ab488 1736 break
39236c6e
A
1737 kern.symbolicator = None
1738 return
1739
1740 if len(cmd_args) < 1:
1741 raise ArgumentError("No arguments specified.")
1742
1743 uuid = cmd_args[0].lower()
1744
1745 load_all_kexts = False
1746 if uuid == "all":
1747 load_all_kexts = True
39037602 1748
39236c6e
A
1749 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
1750 raise ArgumentError("Unknown argument {:s}".format(uuid))
1751
1752 for k_info in all_kexts_info:
1753 cur_uuid = k_info[0].lower()
1754 if load_all_kexts or (uuid == cur_uuid):
d9a64523
A
1755 if (kernel_uuid != cur_uuid):
1756 print "Fetching dSYM for %s" % cur_uuid
1757 info = dsymForUUID(cur_uuid)
1758 if info and 'DBGSymbolRichExecutable' in info:
1759 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1760 addDSYM(cur_uuid, info)
1761 loadDSYM(cur_uuid, int(k_info[1],16), k_info[4])
1762 else:
1763 print "Failed to get symbol info for %s" % cur_uuid
39236c6e
A
1764 #end of for loop
1765 kern.symbolicator = None
1766 return True
1767
39037602 1768
39236c6e
A
1769
1770lldb_alias('showkmod', 'showkmodaddr')
1771lldb_alias('showkext', 'showkmodaddr')
1772lldb_alias('showkextaddr', 'showkmodaddr')
1773
1774@lldb_type_summary(['mount *'])
fe8ab488 1775@header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s} {9: <30s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom', 'iosched supported'))
39236c6e 1776def GetMountSummary(mount):
5ba3f43e 1777 """ Display a summary of mount on the system
39236c6e
A
1778 """
1779 out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1780 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
fe8ab488 1781 "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s} {iomode: <30s}").format(mnt=mount, vfs=mount.mnt_vfsstat, iomode=('Yes' if (mount.mnt_ioflags & 0x4) else 'No'))
39236c6e
A
1782 return out_string
1783
1784@lldb_command('showallmounts')
1785def ShowAllMounts(cmd_args=None):
1786 """ Print all mount points
1787 """
1788 mntlist = kern.globals.mountlist
1789 print GetMountSummary.header
1790 for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1791 print GetMountSummary(mnt)
1792 return
1793
1794lldb_alias('ShowAllVols', 'showallmounts')
1795
1796@lldb_command('systemlog')
1797def ShowSystemLog(cmd_args=None):
1798 """ Display the kernel's printf ring buffer """
1799 msgbufp = kern.globals.msgbufp
1800 msg_size = int(msgbufp.msg_size)
1801 msg_bufx = int(msgbufp.msg_bufx)
1802 msg_bufr = int(msgbufp.msg_bufr)
1803 msg_bufc = msgbufp.msg_bufc
1804 msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size)
1805
1806 # the buffer is circular; start at the write pointer to end,
1807 # then from beginning to write pointer
1808 line = ''
1809 err = lldb.SBError()
1810 for i in range(msg_bufx, msg_size) + range(0, msg_bufx) :
1811 err.Clear()
1812 cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
1813 if not err.Success() :
fe8ab488 1814 raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString())
39236c6e
A
1815 c = chr(cbyte)
1816 if c == '\0' :
1817 continue
1818 elif c == '\n' :
1819 print line
1820 line = ''
1821 else :
1822 line += c
1823
1824 if len(line) > 0 :
1825 print line
1826
1827 return
1828
1829@static_var('output','')
1830def _GetVnodePathName(vnode, vnodename):
1831 """ Internal function to get vnode path string from vnode structure.
1832 params:
1833 vnode - core.value
1834 vnodename - str
1835 returns Nothing. The output will be stored in the static variable.
1836 """
1837 if not vnode:
1838 return
1839 if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0:
1840 if int(vnode.v_mount.mnt_vnodecovered):
1841 _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
1842 else:
1843 _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
5ba3f43e 1844 _GetVnodePathName.output += "/%s" % vnodename
39236c6e
A
1845
1846def GetVnodePath(vnode):
1847 """ Get string representation of the vnode
1848 params: vnodeval - value representing vnode * in the kernel
1849 return: str - of format /path/to/something
1850 """
1851 out_str = ''
1852 if vnode:
1853 if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) :
1854 out_str += "/"
1855 else:
1856 _GetVnodePathName.output = ''
1857 if abs(vnode.v_name) != 0:
1858 _GetVnodePathName(vnode, str(vnode.v_name))
1859 out_str += _GetVnodePathName.output
1860 else:
1861 out_str += 'v_name = NULL'
1862 _GetVnodePathName.output = ''
1863 return out_str
1864
1865
1866@lldb_command('showvnodepath')
1867def ShowVnodePath(cmd_args=None):
1868 """ Prints the path for a vnode
1869 usage: showvnodepath <vnode>
1870 """
1871 if cmd_args != None and len(cmd_args) > 0 :
1872 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1873 if vnode_val:
1874 print GetVnodePath(vnode_val)
1875 return
1876
1877# Macro: showvnodedev
1878def GetVnodeDevInfo(vnode):
1879 """ Internal function to get information from the device type vnodes
1880 params: vnode - value representing struct vnode *
1881 return: str - formatted output information for block and char vnode types passed as param
1882 """
1883 vnodedev_output = ""
1884 vblk_type = GetEnumValue('vtype::VBLK')
1885 vchr_type = GetEnumValue('vtype::VCHR')
1886 if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type):
1887 devnode = Cast(vnode.v_data, 'devnode_t *')
1888 devnode_dev = devnode.dn_typeinfo.dev
1889 devnode_major = (devnode_dev >> 24) & 0xff
1890 devnode_minor = devnode_dev & 0x00ffffff
1891
5ba3f43e 1892 # boilerplate device information for a vnode
39236c6e
A
1893 vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
1894 vnodedev_output += "\n\t type:\t\t"
1895 if (vnode.v_type == vblk_type):
1896 vnodedev_output += "VBLK"
1897 if (vnode.v_type == vchr_type):
1898 vnodedev_output += "VCHR"
1899 vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name)
1900 vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor)
1901 vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode))
1902 vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid)
1903
1904 # decode device specific data
1905 vnodedev_output += "\nDevice Specific Information:\t"
1906 if (vnode.v_type == vblk_type):
1907 vnodedev_output += "Sorry, I do not know how to decode block devices yet!"
1908 vnodedev_output += "\nMaybe you can write me!"
1909
1910 if (vnode.v_type == vchr_type):
1911 # Device information; this is scanty
1912 # range check
1913 if (devnode_major > 42) or (devnode_major < 0):
1914 vnodedev_output += "Invalid major #\n"
1915 # static assignments in conf
1916 elif (devnode_major == 0):
1917 vnodedev_output += "Console mux device\n"
1918 elif (devnode_major == 2):
1919 vnodedev_output += "Current tty alias\n"
1920 elif (devnode_major == 3):
1921 vnodedev_output += "NULL device\n"
1922 elif (devnode_major == 4):
1923 vnodedev_output += "Old pty slave\n"
1924 elif (devnode_major == 5):
1925 vnodedev_output += "Old pty master\n"
1926 elif (devnode_major == 6):
1927 vnodedev_output += "Kernel log\n"
1928 elif (devnode_major == 12):
1929 vnodedev_output += "Memory devices\n"
1930 # Statically linked dynamic assignments
1931 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')):
1932 vnodedev_output += "Cloning pty master not done\n"
1933 #GetVnodeDevCpty(devnode_major, devnode_minor)
1934 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')):
1935 vnodedev_output += "Cloning pty slave not done\n"
1936 #GetVnodeDevCpty(devnode_major, devnode_minor)
1937 else:
1938 vnodedev_output += "RESERVED SLOT\n"
1939 else:
1940 vnodedev_output += "{:#x} is not a device".format(vnode)
1941 return vnodedev_output
1942
1943@lldb_command('showvnodedev')
1944def ShowVnodeDev(cmd_args=None):
1945 """ Routine to display details of all vnodes of block and character device types
1946 Usage: showvnodedev <address of vnode>
1947 """
1948 if not cmd_args:
1949 print "No arguments passed"
1950 print ShowVnodeDev.__doc__
1951 return False
1952 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1953 if not vnode_val:
1954 print "unknown arguments:", str(cmd_args)
1955 return False
1956 print GetVnodeDevInfo(vnode_val)
1957
1958# EndMacro: showvnodedev
1959
1960# Macro: showvnodelocks
1961def GetVnodeLock(lockf):
1962 """ Internal function to get information from the given advisory lock
1963 params: lockf - value representing v_lockf member in struct vnode *
1964 return: str - formatted output information for the advisory lock
1965 """
1966 vnode_lock_output = ''
1967 lockf_flags = lockf.lf_flags
1968 lockf_type = lockf.lf_type
1969 if lockf_flags & 0x20:
1970 vnode_lock_output += ("{: <8s}").format('flock')
1971 if lockf_flags & 0x40:
1972 vnode_lock_output += ("{: <8s}").format('posix')
1973 if lockf_flags & 0x80:
1974 vnode_lock_output += ("{: <8s}").format('prov')
1975 if lockf_flags & 0x10:
1976 vnode_lock_output += ("{: <4s}").format('W')
3e170ce0
A
1977 if lockf_flags & 0x400:
1978 vnode_lock_output += ("{: <8s}").format('ofd')
39236c6e
A
1979 else:
1980 vnode_lock_output += ("{: <4s}").format('.')
1981
1982 # POSIX file vs advisory range locks
1983 if lockf_flags & 0x40:
1984 lockf_proc = Cast(lockf.lf_id, 'proc *')
1985 vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
1986 else:
1987 vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
5ba3f43e 1988
39236c6e
A
1989 # lock type
1990 if lockf_type == 1:
1991 vnode_lock_output += ("{: <12s}").format('shared')
1992 else:
1993 if lockf_type == 3:
1994 vnode_lock_output += ("{: <12s}").format('exclusive')
1995 else:
1996 if lockf_type == 2:
1997 vnode_lock_output += ("{: <12s}").format('unlock')
1998 else:
1999 vnode_lock_output += ("{: <12s}").format('unknown')
5ba3f43e 2000
39236c6e
A
2001 # start and stop values
2002 vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
2003 vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
2004 return vnode_lock_output
2005
2006@header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
2007def GetVnodeLocksSummary(vnode):
2008 """ Internal function to get summary of advisory locks for the given vnode
2009 params: vnode - value representing the vnode object
2010 return: str - formatted output information for the summary of advisory locks
2011 """
2012 out_str = ''
2013 if vnode:
2014 lockf_list = vnode.v_lockf
2015 for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'):
2016 out_str += ("{: <4s}").format('H')
2017 out_str += GetVnodeLock(lockf_itr)
2018 lockf_blocker = lockf_itr.lf_blkhd.tqh_first
2019 while lockf_blocker:
2020 out_str += ("{: <4s}").format('>')
2021 out_str += GetVnodeLock(lockf_blocker)
5ba3f43e 2022 lockf_blocker = lockf_blocker.lf_block.tqe_next
39236c6e
A
2023 return out_str
2024
2025@lldb_command('showvnodelocks')
2026def ShowVnodeLocks(cmd_args=None):
2027 """ Routine to display list of advisory record locks for the given vnode address
2028 Usage: showvnodelocks <address of vnode>
2029 """
2030 if not cmd_args:
2031 print "No arguments passed"
2032 print ShowVnodeLocks.__doc__
2033 return False
2034 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
2035 if not vnode_val:
2036 print "unknown arguments:", str(cmd_args)
2037 return False
2038 print GetVnodeLocksSummary.header
2039 print GetVnodeLocksSummary(vnode_val)
2040
2041# EndMacro: showvnodelocks
2042
2043# Macro: showproclocks
5ba3f43e 2044
39236c6e
A
2045@lldb_command('showproclocks')
2046def ShowProcLocks(cmd_args=None):
2047 """ Routine to display list of advisory record locks for the given process
2048 Usage: showproclocks <address of proc>
2049 """
2050 if not cmd_args:
2051 print "No arguments passed"
2052 print ShowProcLocks.__doc__
2053 return False
2054 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *')
2055 if not proc:
2056 print "unknown arguments:", str(cmd_args)
2057 return False
2058 out_str = ''
2059 proc_filedesc = proc.p_fd
2060 fd_lastfile = proc_filedesc.fd_lastfile
2061 fd_ofiles = proc_filedesc.fd_ofiles
2062 count = 0
2063 seen = 0
2064 while count <= fd_lastfile:
2065 if fd_ofiles[count]:
2066 fglob = fd_ofiles[count].f_fglob
2067 fo_type = fglob.fg_ops.fo_type
2068 if fo_type == 1:
2069 fg_data = fglob.fg_data
2070 fg_vnode = Cast(fg_data, 'vnode *')
2071 name = fg_vnode.v_name
2072 lockf_itr = fg_vnode.v_lockf
2073 if lockf_itr:
2074 if not seen:
2075 print GetVnodeLocksSummary.header
2076 seen = seen + 1
2077 out_str += ("\n( fd {:d}, name ").format(count)
2078 if not name:
2079 out_str += "(null) )\n"
2080 else:
2081 out_str += "{:s} )\n".format(name)
2082 print out_str
2083 print GetVnodeLocksSummary(fg_vnode)
2084 count = count + 1
2085 print "\n{0: d} total locks for {1: #018x}".format(seen, proc)
2086
2087# EndMacro: showproclocks
2088
2089@lldb_type_summary(['vnode_t', 'vnode *'])
fe8ab488 2090@header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <6s} {8: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name'))
39236c6e
A
2091def GetVnodeSummary(vnode):
2092 """ Get a summary of important information out of vnode
2093 """
2094 out_str = ''
fe8ab488 2095 format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <6s} {8: <35s}"
39236c6e
A
2096 usecount = int(vnode.v_usecount)
2097 iocount = int(vnode.v_iocount)
2098 v_data_ptr = int(hex(vnode.v_data), 16)
2099 vtype = int(vnode.v_type)
2100 vtype_str = "%d" % vtype
2101 vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
2102 if vtype >= 0 and vtype < len(vnode_types):
2103 vtype_str = vnode_types[vtype]
2104 parent_ptr = int(hex(vnode.v_parent), 16)
2105 name_ptr = int(hex(vnode.v_name), 16)
2106 name =""
2107 if name_ptr != 0:
2108 name = str(vnode.v_name)
2109 elif int(vnode.v_tag) == 16 :
2110 cnode = Cast(vnode.v_data, 'cnode *')
2111 name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
2112 mapped = '-'
fe8ab488 2113 csblob_version = '-'
39236c6e 2114 if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
fe8ab488 2115 csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen)
5ba3f43e 2116 # Check to see if vnode is mapped/unmapped
39236c6e
A
2117 if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
2118 mapped = '1'
2119 else:
2120 mapped = '0'
fe8ab488 2121 out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name)
39236c6e
A
2122 return out_str
2123
2124@lldb_command('showallvnodes')
2125def ShowAllVnodes(cmd_args=None):
2126 """ Display info about all vnodes
2127 """
2128 mntlist = kern.globals.mountlist
2129 print GetVnodeSummary.header
2130 for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
2131 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2132 print GetVnodeSummary(vnodeval)
2133 return
2134
2135@lldb_command('showvnode')
2136def ShowVnode(cmd_args=None):
2137 """ Display info about one vnode
2138 usage: showvnode <vnode>
2139 """
2140 if cmd_args == None or len(cmd_args) < 1:
2141 print "Please provide valid vnode argument. Type help showvnode for help."
2142 return
2143 vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
2144 print GetVnodeSummary.header
2145 print GetVnodeSummary(vnodeval)
5ba3f43e 2146
39236c6e
A
2147@lldb_command('showvolvnodes')
2148def ShowVolVnodes(cmd_args=None):
2149 """ Display info about all vnodes of a given mount_t
2150 """
2151 if cmd_args == None or len(cmd_args) < 1:
2152 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2153 return
2154 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2155 print GetVnodeSummary.header
2156 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2157 print GetVnodeSummary(vnodeval)
2158 return
2159
2160@lldb_command('showvolbusyvnodes')
2161def ShowVolBusyVnodes(cmd_args=None):
2162 """ Display info about busy (iocount!=0) vnodes of a given mount_t
2163 """
2164 if cmd_args == None or len(cmd_args) < 1:
2165 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2166 return
2167 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2168 print GetVnodeSummary.header
2169 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2170 if int(vnodeval.v_iocount) != 0:
2171 print GetVnodeSummary(vnodeval)
2172
2173@lldb_command('showallbusyvnodes')
2174def ShowAllBusyVnodes(cmd_args=None):
2175 """ Display info about all busy (iocount!=0) vnodes
2176 """
2177 mntlistval = kern.globals.mountlist
2178 for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'):
2179 ShowVolBusyVnodes([hex(mntval)])
2180
2181@lldb_command('print_vnode')
2182def PrintVnode(cmd_args=None):
2183 """ Prints out the fields of a vnode struct
2184 Usage: print_vnode <vnode>
2185 """
2186 if not cmd_args:
2187 print "Please provide valid vnode argument. Type help print_vnode for help."
2188 return
2189 ShowVnode(cmd_args)
2190
2191@lldb_command('showworkqvnodes')
2192def ShowWorkqVnodes(cmd_args=None):
2193 """ Print the vnode worker list
2194 Usage: showworkqvnodes <struct mount *>
2195 """
2196 if not cmd_args:
2197 print "Please provide valid mount argument. Type help showworkqvnodes for help."
2198 return
2199
2200 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2201 vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *')
2202 print GetVnodeSummary.header
2203 while int(vp) != 0:
2204 print GetVnodeSummary(vp)
2205 vp = vp.v_mntvnodes.tqe_next
2206
2207@lldb_command('shownewvnodes')
2208def ShowNewVnodes(cmd_args=None):
2209 """ Print the new vnode list
2210 Usage: shownewvnodes <struct mount *>
2211 """
2212 if not cmd_args:
2213 print "Please provide valid mount argument. Type help shownewvnodes for help."
2214 return
2215 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2216 vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *')
2217 print GetVnodeSummary.header
2218 while int(vp) != 0:
2219 print GetVnodeSummary(vp)
2220 vp = vp.v_mntvnodes.tqe_next
2221
2222
2223@lldb_command('showprocvnodes')
2224def ShowProcVnodes(cmd_args=None):
2225 """ Routine to print out all the open fds which are vnodes in a process
2226 Usage: showprocvnodes <proc *>
2227 """
2228 if not cmd_args:
2229 print "Please provide valid proc argument. Type help showprocvnodes for help."
2230 return
2231 procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *')
2232 fdptr = Cast(procptr.p_fd, 'filedesc *')
2233 if int(fdptr.fd_cdir) != 0:
2234 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir))
2235 if int(fdptr.fd_rdir) != 0:
2236 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
2237 count = 0
cb323159 2238 print '\n' + '{0: <5s} {1: <7s} {2: <20s} '.format('fd', 'flags', 'fileglob') + GetVnodeSummary.header
39236c6e 2239 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
cb323159 2240 fpptr = Cast(fdptr.fd_ofiles, 'uint64_t *')
39236c6e
A
2241 while count < fdptr.fd_nfiles:
2242 fpp = dereference(fpptr)
cb323159 2243 fproc = kern.GetValueFromAddress(int(fpp), 'fileproc *')
39236c6e
A
2244 if int(fproc) != 0:
2245 fglob = dereference(fproc).f_fglob
2246 flags = ""
2247 if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
2248 if (fdptr.fd_ofileflags[count] & 1): flags += 'E'
2249 if (fdptr.fd_ofileflags[count] & 2): flags += 'F'
2250 if (fdptr.fd_ofileflags[count] & 4): flags += 'R'
2251 if (fdptr.fd_ofileflags[count] & 8): flags += 'C'
cb323159 2252 print '{0: <5d} {1: <7s} {2: <#020x} '.format(count, flags, fglob) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
39236c6e 2253 count += 1
cb323159 2254 fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'uint64_t *')
39236c6e
A
2255
2256@lldb_command('showallprocvnodes')
2257def ShowAllProcVnodes(cmd_args=None):
2258 """ Routine to print out all the open fds which are vnodes
2259 """
2260
2261 procptr = Cast(kern.globals.allproc.lh_first, 'proc *')
2262 while procptr and int(procptr) != 0:
2263 print '{:<s}'.format("=" * 106)
2264 print GetProcInfo(procptr)
2265 ShowProcVnodes([int(procptr)])
2266 procptr = procptr.p_list.le_next
2267
2268@xnudebug_test('test_vnode')
2269def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
2270 """ Test the functionality of vnode related commands
5ba3f43e 2271 returns
39236c6e 2272 - False on failure
5ba3f43e 2273 - True on success
39236c6e
A
2274 """
2275 if not isConnected:
2276 print "Target is not connected. Cannot test memstats"
2277 return False
2278 res = lldb.SBCommandReturnObject()
2279 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res)
2280 result = res.GetOutput()
2281 if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
2282 return True
5ba3f43e 2283 else:
39236c6e
A
2284 return False
2285
2286# Macro: showallmtx
2287@lldb_type_summary(['_lck_grp_ *'])
2288def GetMutexEntry(mtxg):
2289 """ Summarize a mutex group entry with important information.
2290 params:
2291 mtxg: value - obj representing a mutex group in kernel
2292 returns:
2293 out_string - summary of the mutex group
2294 """
2295 out_string = ""
2296
2297 if kern.ptrsize == 8:
2298 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2299 else:
2300 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2301
2302 if mtxg.lck_grp_mtxcnt:
2303 out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt,
2304 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt,
2305 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name)
2306 return out_string
2307
2308@lldb_command('showallmtx')
2309def ShowAllMtx(cmd_args=None):
2310 """ Routine to print a summary listing of all mutexes
2311 """
2312
2313 if kern.ptrsize == 8:
2314 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2315 else:
2316 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
5ba3f43e
A
2317
2318 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
39236c6e
A
2319
2320 mtxgrp_queue_head = kern.globals.lck_grp_queue
5ba3f43e
A
2321 mtxgrp_ptr_type = GetType('_lck_grp_ *')
2322
2323 for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
39236c6e
A
2324 print GetMutexEntry(mtxgrp_ptr)
2325 return
2326# EndMacro: showallmtx
2327
2328# Macro: showallrwlck
2329@lldb_type_summary(['_lck_grp_ *'])
2330def GetRWLEntry(rwlg):
2331 """ Summarize a reader writer lock group with important information.
2332 params:
2333 rwlg: value - obj representing a reader writer lock group in kernel
2334 returns:
2335 out_string - summary of the reader writer lock group
2336 """
2337 out_string = ""
2338
2339 if kern.ptrsize == 8:
2340 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2341 else:
2342 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2343
2344 if rwlg.lck_grp_rwcnt:
2345 out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt,
2346 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt,
2347 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
2348 return out_string
2349
15129b1c
A
2350#Macro: showlock
2351@lldb_type_summary(['lck_mtx_t *'])
2352@header("===== Mutex Lock Summary =====")
2353def GetMutexLockSummary(mtx):
2354 """ Summarize mutex lock with important information.
2355 params:
2356 mtx: value - obj representing a mutex lock in kernel
2357 returns:
2358 out_str - summary of the mutex lock
2359 """
2360 if not mtx:
2361 return "Invalid lock value: 0x0"
2362
2363 if kern.arch == "x86_64":
39037602
A
2364 out_str = "Lock Type : MUTEX\n"
2365 if mtx.lck_mtx_tag == 0x07ff1007 :
2366 out_str += "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx.lck_mtx_ptr)
2367 mtx = Cast(mtx.lck_mtx_ptr, 'lck_mtx_t *')
2368
2369 if mtx.lck_mtx_tag == 0x07fe2007 :
2370 out_str += "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx.lck_mtx_tag)
2371
2372 out_str += "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx=mtx)
2373 out_str += "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx=mtx)
2374 out_str += "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx=mtx)
2375 out_str += "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx=mtx)
2376 out_str += "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx=mtx)
2377 out_str += "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx=mtx)
2378 out_str += "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx=mtx)
2379 out_str += "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx=mtx)
d9a64523
A
2380 if mtx.lck_mtx_pad32 == 0xFFFFFFFF :
2381 out_str += "Canary (valid) : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
39037602 2382 else:
d9a64523 2383 out_str += "Canary (INVALID) : {mtx.lck_mtx_pad32:#x}\n".format(mtx=mtx)
15129b1c
A
2384 return out_str
2385
2386 out_str = "Lock Type\t\t: MUTEX\n"
39037602
A
2387 out_str += "Owner Thread\t\t: {:#x}".format(mtx.lck_mtx_data & ~0x3)
2388 if (mtx.lck_mtx_data & ~0x3) == 0xfffffff0:
2389 out_str += " Held as spinlock"
2390 out_str += "\nNumber of Waiters\t: {:d}\n".format(mtx.lck_mtx_waiters)
15129b1c 2391 out_str += "Flags\t\t\t: "
39037602 2392 if mtx.lck_mtx_data & 0x1:
15129b1c 2393 out_str += "[Interlock Locked] "
39037602 2394 if mtx.lck_mtx_data & 0x2:
15129b1c 2395 out_str += "[Wait Flag]"
15129b1c
A
2396 return out_str
2397
2398@lldb_type_summary(['lck_spin_t *'])
2399@header("===== SpinLock Summary =====")
2400def GetSpinLockSummary(spinlock):
2401 """ Summarize spinlock with important information.
2402 params:
2403 spinlock: value - obj representing a spinlock in kernel
2404 returns:
2405 out_str - summary of the spinlock
2406 """
2407 if not spinlock:
2408 return "Invalid lock value: 0x0"
2409
2410 out_str = "Lock Type\t\t: SPINLOCK\n"
2411 if kern.arch == "x86_64":
2412 out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
2413 return out_str
2414
39037602
A
2415 lock_data = spinlock.hwlock.lock_data
2416 if lock_data == 1:
2417 out_str += "Invalid state: interlock is locked but no owner\n"
2418 return out_str
2419 out_str += "Owner Thread\t\t: "
2420 if lock_data == 0:
2421 out_str += "None\n"
2422 else:
2423 out_str += "{:#x}\n".format(lock_data & ~0x1)
2424 if (lock_data & 1) == 0:
2425 out_str += "Invalid state: owned but interlock bit is not set\n"
15129b1c
A
2426 return out_str
2427
2428@lldb_command('showlock', 'MS')
2429def ShowLock(cmd_args=None, cmd_options={}):
2430 """ Show info about a lock - its state and owner thread details
2431 Usage: showlock <address of a lock>
2432 -M : to consider <addr> as lck_mtx_t
2433 -S : to consider <addr> as lck_spin_t
2434 """
2435 if not cmd_args:
2436 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
2437 return
2438
2439 summary_str = ""
d9a64523
A
2440 addr = cmd_args[0]
2441 # from osfmk/arm/locks.h
2442 LCK_SPIN_TYPE = 0x11
2443 LCK_MTX_TYPE = 0x22
2444 if kern.arch == "x86_64":
15129b1c 2445 if "-M" in cmd_options:
d9a64523 2446 lock_mtx = kern.GetValueFromAddress(addr, 'lck_mtx_t *')
15129b1c
A
2447 summary_str = GetMutexLockSummary(lock_mtx)
2448 elif "-S" in cmd_options:
d9a64523 2449 lock_spin = kern.GetValueFromAddress(addr, 'lck_spin_t *')
15129b1c
A
2450 summary_str = GetSpinLockSummary(lock_spin)
2451 else:
2452 summary_str = "Please specify supported lock option(-M/-S)"
2453
2454 print summary_str
d9a64523
A
2455 else:
2456 lock = kern.GetValueFromAddress(addr, 'uintptr_t *')
2457 if lock:
2458 lock_mtx = Cast(lock, 'lck_mtx_t*')
2459 if lock_mtx.lck_mtx_type == LCK_MTX_TYPE:
2460 summary_str = GetMutexLockSummary(lock_mtx)
2461
2462 lock_spin = Cast(lock, 'lck_spin_t*')
2463 if lock_spin.type == LCK_SPIN_TYPE:
2464 summary_str = GetSpinLockSummary(lock_spin)
2465 if summary_str == "":
2466 summary_str = "Lock Type\t\t: INVALID LOCK"
2467 print summary_str
15129b1c
A
2468
2469#EndMacro: showlock
2470
39236c6e
A
2471@lldb_command('showallrwlck')
2472def ShowAllRWLck(cmd_args=None):
2473 """ Routine to print a summary listing of all read/writer locks
2474 """
2475 if kern.ptrsize == 8:
2476 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2477 else:
2478 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2479
2480 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2481
2482 rwlgrp_queue_head = kern.globals.lck_grp_queue
2483 rwlgrp_ptr_type = GetType('_lck_grp_ *')
2484 for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"):
2485 print GetRWLEntry(rwlgrp_ptr)
2486 return
2487# EndMacro: showallrwlck
2488
2489#Macro: showbootermemorymap
2490@lldb_command('showbootermemorymap')
2491def ShowBooterMemoryMap(cmd_args=None):
2492 """ Prints out the phys memory map from kernelBootArgs
2493 Supported only on x86_64
2494 """
5ba3f43e 2495 if kern.arch != 'x86_64':
39236c6e
A
2496 print "showbootermemorymap not supported on this architecture"
2497 return
5ba3f43e 2498
39236c6e
A
2499 out_string = ""
2500
2501 # Memory type map
2502 memtype_dict = {
2503 0: 'Reserved',
2504 1: 'LoaderCode',
2505 2: 'LoaderData',
2506 3: 'BS_code',
2507 4: 'BS_data',
2508 5: 'RT_code',
2509 6: 'RT_data',
2510 7: 'Convention',
2511 8: 'Unusable',
2512 9: 'ACPI_recl',
2513 10: 'ACPI_NVS',
2514 11: 'MemMapIO',
2515 12: 'MemPortIO',
2516 13: 'PAL_code'
2517 }
2518
2519 boot_args = kern.globals.kernelBootArgs
2520 msize = boot_args.MemoryMapDescriptorSize
2521 mcount = (boot_args.MemoryMapSize) / unsigned(msize)
2522
2523 out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2524
2525 i = 0
2526 while i < mcount:
5ba3f43e 2527 mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + kern.VM_MIN_KERNEL_ADDRESS + unsigned(i*msize), 'EfiMemoryRange *')
39236c6e
A
2528 mtype = unsigned(mptr.Type)
2529 if mtype in memtype_dict:
2530 out_string += "{0: <12s}".format(memtype_dict[mtype])
2531 else:
2532 out_string += "{0: <12s}".format("UNKNOWN")
2533
2534 if mptr.VirtualStart == 0:
2535 out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute)
2536 else:
2537 out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute)
2538 i = i + 1
2539
2540 print out_string
2541#EndMacro: showbootermemorymap
2542
fe8ab488
A
2543@lldb_command('show_all_purgeable_objects')
2544def ShowAllPurgeableVmObjects(cmd_args=None):
2545 """ Routine to print a summary listing of all the purgeable vm objects
2546 """
2547 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2548 ShowAllPurgeableVolatileVmObjects()
2549 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2550 ShowAllPurgeableNonVolatileVmObjects()
2551
2552@lldb_command('show_all_purgeable_nonvolatile_objects')
2553def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
2554 """ Routine to print a summary listing of all the vm objects in
2555 the purgeable_nonvolatile_queue
2556 """
2557
2558 nonvolatile_total = lambda:None
2559 nonvolatile_total.objects = 0
2560 nonvolatile_total.vsize = 0
2561 nonvolatile_total.rsize = 0
2562 nonvolatile_total.wsize = 0
2563 nonvolatile_total.csize = 0
2564 nonvolatile_total.disowned_objects = 0
2565 nonvolatile_total.disowned_vsize = 0
2566 nonvolatile_total.disowned_rsize = 0
2567 nonvolatile_total.disowned_wsize = 0
2568 nonvolatile_total.disowned_csize = 0
2569
2570 queue_len = kern.globals.purgeable_nonvolatile_count
2571 queue_head = kern.globals.purgeable_nonvolatile_queue
2572
d9a64523 2573 print 'purgeable_nonvolatile_queue:{: <#018x} purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
fe8ab488
A
2574 print 'N:non-volatile V:volatile E:empty D:deny\n'
2575
d9a64523 2576 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
fe8ab488
A
2577 idx = 0
2578 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
2579 idx += 1
2580 ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
2581 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.disowned_objects, nonvolatile_total.disowned_vsize, nonvolatile_total.disowned_rsize, nonvolatile_total.disowned_wsize, nonvolatile_total.disowned_csize)
2582 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.objects, nonvolatile_total.vsize, nonvolatile_total.rsize, nonvolatile_total.wsize, nonvolatile_total.csize)
2583
2584
2585def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
2586 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
2587 params:
2588 object - core.value : a object of type 'struct vm_object *'
2589 returns:
2590 None
2591 """
3e170ce0 2592 page_size = kern.globals.page_size
fe8ab488
A
2593 if object.purgable == 0:
2594 purgable = "N"
2595 elif object.purgable == 1:
2596 purgable = "V"
2597 elif object.purgable == 2:
2598 purgable = "E"
2599 elif object.purgable == 3:
2600 purgable = "D"
2601 else:
2602 purgable = "?"
2603 if object.pager == 0:
2604 compressed_count = 0
2605 else:
2606 compressor_pager = Cast(object.pager, 'compressor_pager *')
2607 compressed_count = compressor_pager.cpgr_num_slots_occupied
2608
d9a64523 2609 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
fe8ab488
A
2610
2611 nonvolatile_total.objects += 1
3e170ce0 2612 nonvolatile_total.vsize += object.vo_un1.vou_size/page_size
fe8ab488
A
2613 nonvolatile_total.rsize += object.resident_page_count
2614 nonvolatile_total.wsize += object.wired_page_count
2615 nonvolatile_total.csize += compressed_count
d9a64523 2616 if object.vo_un2.vou_owner == 0:
fe8ab488 2617 nonvolatile_total.disowned_objects += 1
3e170ce0 2618 nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
fe8ab488
A
2619 nonvolatile_total.disowned_rsize += object.resident_page_count
2620 nonvolatile_total.disowned_wsize += object.wired_page_count
2621 nonvolatile_total.disowned_csize += compressed_count
2622
2623
2624@lldb_command('show_all_purgeable_volatile_objects')
2625def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
2626 """ Routine to print a summary listing of all the vm objects in
2627 the purgeable queues
2628 """
2629 volatile_total = lambda:None
2630 volatile_total.objects = 0
2631 volatile_total.vsize = 0
2632 volatile_total.rsize = 0
2633 volatile_total.wsize = 0
2634 volatile_total.csize = 0
2635 volatile_total.disowned_objects = 0
2636 volatile_total.disowned_vsize = 0
2637 volatile_total.disowned_rsize = 0
2638 volatile_total.disowned_wsize = 0
2639 volatile_total.disowned_csize = 0
2640
2641 purgeable_queues = kern.globals.purgeable_queues
2642 print "---------- OBSOLETE\n"
2643 ShowPurgeableQueue(purgeable_queues[0], volatile_total)
2644 print "\n\n---------- FIFO\n"
2645 ShowPurgeableQueue(purgeable_queues[1], volatile_total)
2646 print "\n\n---------- LIFO\n"
2647 ShowPurgeableQueue(purgeable_queues[2], volatile_total)
2648
2649 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.disowned_objects, volatile_total.disowned_vsize, volatile_total.disowned_rsize, volatile_total.disowned_wsize, volatile_total.disowned_csize)
2650 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.objects, volatile_total.vsize, volatile_total.rsize, volatile_total.wsize, volatile_total.csize)
2651 purgeable_count = kern.globals.vm_page_purgeable_count
2652 purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
2653 if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
2654 mismatch = "<--------- MISMATCH\n"
2655 else:
2656 mismatch = ""
2657 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
2658
2659
2660def ShowPurgeableQueue(qhead, volatile_total):
2661 print "----- GROUP 0\n"
2662 ShowPurgeableGroup(qhead.objq[0], volatile_total)
2663 print "----- GROUP 1\n"
2664 ShowPurgeableGroup(qhead.objq[1], volatile_total)
2665 print "----- GROUP 2\n"
2666 ShowPurgeableGroup(qhead.objq[2], volatile_total)
2667 print "----- GROUP 3\n"
2668 ShowPurgeableGroup(qhead.objq[3], volatile_total)
2669 print "----- GROUP 4\n"
2670 ShowPurgeableGroup(qhead.objq[4], volatile_total)
2671 print "----- GROUP 5\n"
2672 ShowPurgeableGroup(qhead.objq[5], volatile_total)
2673 print "----- GROUP 6\n"
2674 ShowPurgeableGroup(qhead.objq[6], volatile_total)
2675 print "----- GROUP 7\n"
2676 ShowPurgeableGroup(qhead.objq[7], volatile_total)
2677
2678def ShowPurgeableGroup(qhead, volatile_total):
2679 idx = 0
2680 for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
2681 if idx == 0:
2682# print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s} {:18s} {:>6s} {:<20s} {:s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process","volatilizer","pid","process","")
d9a64523 2683 print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
fe8ab488
A
2684 idx += 1
2685 ShowPurgeableVolatileVmObject(object, idx, volatile_total)
2686
2687def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
2688 """ Routine to print out a summary a VM object in a purgeable queue
2689 params:
2690 object - core.value : a object of type 'struct vm_object *'
2691 returns:
2692 None
2693 """
d9a64523 2694## if int(object.vo_un2.vou_owner) != int(object.vo_purgeable_volatilizer):
fe8ab488 2695# diff=" !="
5ba3f43e 2696## else:
fe8ab488 2697# diff=" "
3e170ce0 2698 page_size = kern.globals.page_size
fe8ab488
A
2699 if object.purgable == 0:
2700 purgable = "N"
2701 elif object.purgable == 1:
2702 purgable = "V"
2703 elif object.purgable == 2:
2704 purgable = "E"
2705 elif object.purgable == 3:
2706 purgable = "D"
2707 else:
2708 purgable = "?"
2709 if object.pager == 0:
2710 compressed_count = 0
2711 else:
2712 compressor_pager = Cast(object.pager, 'compressor_pager *')
2713 compressed_count = compressor_pager.cpgr_num_slots_occupied
d9a64523
A
2714# print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {: <#018x} {:>6d} {:<20s} {: <#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner),object.vo_purgeable_volatilizer,GetProcPIDForObjectOwner(object.vo_purgeable_volatilizer),GetProcNameForObjectOwner(object.vo_purgeable_volatilizer),diff)
2715 print "{:>6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>3d} {: <#018x} {:>6d} {:<20s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
fe8ab488 2716 volatile_total.objects += 1
3e170ce0 2717 volatile_total.vsize += object.vo_un1.vou_size/page_size
fe8ab488
A
2718 volatile_total.rsize += object.resident_page_count
2719 volatile_total.wsize += object.wired_page_count
2720 volatile_total.csize += compressed_count
d9a64523 2721 if object.vo_un2.vou_owner == 0:
fe8ab488 2722 volatile_total.disowned_objects += 1
3e170ce0 2723 volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
fe8ab488
A
2724 volatile_total.disowned_rsize += object.resident_page_count
2725 volatile_total.disowned_wsize += object.wired_page_count
2726 volatile_total.disowned_csize += compressed_count
2727
2728
2729def GetCompressedPagesForObject(obj):
2730 """Stuff
2731 """
2732 pager = Cast(obj.pager, 'compressor_pager_t')
2733 return pager.cpgr_num_slots_occupied
5ba3f43e
A
2734 """ # commented code below
2735 if pager.cpgr_num_slots > 128:
2736 slots_arr = pager.cpgr_slots.cpgr_islots
2737 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
2738 index = 0
2739 compressor_slot = 0
2740 compressed_pages = 0
2741 while index < num_indirect_slot_ptr:
2742 compressor_slot = 0
2743 if slots_arr[index]:
2744 while compressor_slot < 128:
2745 if slots_arr[index][compressor_slot]:
2746 compressed_pages += 1
2747 compressor_slot += 1
2748 index += 1
2749 else:
2750 slots_arr = pager.cpgr_slots.cpgr_dslots
2751 compressor_slot = 0
2752 compressed_pages = 0
2753 while compressor_slot < pager.cpgr_num_slots:
2754 if slots_arr[compressor_slot]:
2755 compressed_pages += 1
2756 compressor_slot += 1
2757 return compressed_pages
2758 """
fe8ab488 2759
fe8ab488
A
2760def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
2761 """ Routine to print out a summary listing of all the entries in a vm_map
2762 params:
2763 task - core.value : a object of type 'task *'
2764 returns:
2765 None
2766 """
2767 print "vm_map entries for task " + hex(task)
2768 print GetTaskSummary.header
2769 print GetTaskSummary(task)
2770 if not task.map:
2771 print "Task {0: <#020x} has map = 0x0"
2772 return None
d9a64523 2773 showmapvme(task.map, 0, 0, show_pager_info, show_all_shadows, False)
fe8ab488 2774
94ff46dc 2775@lldb_command("showmapvme", "A:B:F:PRST")
fe8ab488
A
2776def ShowMapVME(cmd_args=None, cmd_options={}):
2777 """Routine to print out info about the specified vm_map and its vm entries
d9a64523
A
2778 usage: showmapvme <vm_map> [-A start] [-B end] [-S] [-P]
2779 Use -A <start> flag to start at virtual address <start>
2780 Use -B <end> flag to end at virtual address <end>
94ff46dc 2781 Use -F <virtaddr> flag to find just the VME containing the given VA
3e170ce0
A
2782 Use -S flag to show VM object shadow chains
2783 Use -P flag to show pager info (mapped file, compressed pages, ...)
d9a64523
A
2784 Use -R flag to reverse order
2785 Use -T to show red-black tree pointers
fe8ab488
A
2786 """
2787 if cmd_args == None or len(cmd_args) < 1:
d9a64523 2788 print "Invalid argument.", ShowMapVME.__doc__
fe8ab488
A
2789 return
2790 show_pager_info = False
2791 show_all_shadows = False
d9a64523
A
2792 show_rb_tree = False
2793 start_vaddr = 0
2794 end_vaddr = 0
2795 reverse_order = False
2796 if "-A" in cmd_options:
2797 start_vaddr = unsigned(int(cmd_options['-A'], 16))
2798 if "-B" in cmd_options:
2799 end_vaddr = unsigned(int(cmd_options['-B'], 16))
94ff46dc
A
2800 if "-F" in cmd_options:
2801 start_vaddr = unsigned(int(cmd_options['-F'], 16))
2802 end_vaddr = start_vaddr
fe8ab488
A
2803 if "-P" in cmd_options:
2804 show_pager_info = True
2805 if "-S" in cmd_options:
2806 show_all_shadows = True
d9a64523
A
2807 if "-R" in cmd_options:
2808 reverse_order = True
2809 if "-T" in cmd_options:
2810 show_rb_tree = True
fe8ab488 2811 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
d9a64523
A
2812 showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
2813
2814@lldb_command("showvmobject", "A:B:PRST")
2815def ShowVMObject(cmd_args=None, cmd_options={}):
2816 """Routine to print out a VM object and its shadow chain
2817 usage: showvmobject <vm_object> [-S] [-P]
2818 -S: show VM object shadow chain
2819 -P: show pager info (mapped file, compressed pages, ...)
2820 """
2821 if cmd_args == None or len(cmd_args) < 1:
2822 print "Invalid argument.", ShowMapVME.__doc__
2823 return
2824 show_pager_info = False
2825 show_all_shadows = False
2826 if "-P" in cmd_options:
2827 show_pager_info = True
2828 if "-S" in cmd_options:
2829 show_all_shadows = True
2830 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
2831 showvmobject(object, 0, 0, show_pager_info, show_all_shadows)
fe8ab488 2832
d9a64523 2833def showvmobject(object, offset=0, size=0, show_pager_info=False, show_all_shadows=False):
3e170ce0 2834 page_size = kern.globals.page_size
fe8ab488
A
2835 vnode_pager_ops = kern.globals.vnode_pager_ops
2836 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
d9a64523
A
2837 depth = 0
2838 if size == 0 and object != 0 and object.internal:
2839 size = object.vo_un1.vou_size
2840 while object != 0:
2841 depth += 1
2842 if show_all_shadows == False and depth != 1 and object.shadow != 0:
2843 offset += unsigned(object.vo_un2.vou_shadow_offset)
2844 object = object.shadow
2845 continue
2846 if object.copy_strategy == 0:
2847 copy_strategy="N"
2848 elif object.copy_strategy == 2:
2849 copy_strategy="D"
2850 elif object.copy_strategy == 4:
2851 copy_strategy="S"
2852
2853 else:
2854 copy_strategy=str(object.copy_strategy)
2855 if object.internal:
2856 internal = "internal"
2857 else:
2858 internal = "external"
2859 purgeable = "NVED"[int(object.purgable)]
2860 pager_string = ""
2861 if object.phys_contiguous:
2862 pager_string = pager_string + "phys_contig {:#018x}:{:#018x} ".format(unsigned(object.vo_un2.vou_shadow_offset), unsigned(object.vo_un1.vou_size))
2863 pager = object.pager
2864 if show_pager_info and pager != 0:
2865 if object.internal:
2866 pager_string = pager_string + "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
2867 elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
2868 vnode_pager = Cast(pager,'vnode_pager *')
2869 pager_string = pager_string + "-> " + GetVnodePath(vnode_pager.vnode_handle)
2870 else:
2871 pager_string = pager_string + "-> {:s}:{: <#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager)
2872 print "{:>18d} {:#018x}:{:#018x} {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} purg:{:1s} {:s} wtag:{:d} ({:d} {:d} {:d}) {:s}".format(depth,offset,offset+size,object,object.ref_count,object.true_share,copy_strategy,purgeable,internal,object.wire_tag,unsigned(object.vo_un1.vou_size)/page_size,object.resident_page_count,object.wired_page_count,pager_string)
2873# print " #{:<5d} obj {: <#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} size:{:<10d} wired:{:<10d} resident:{:<10d} reusable:{:<10d}".format(depth,object,object.ref_count,object.true_share,copy_strategy,internal,object.vo_un1.vou_size/page_size,object.wired_page_count,object.resident_page_count,object.reusable_page_count)
2874 offset += unsigned(object.vo_un2.vou_shadow_offset)
2875 object = object.shadow
2876
2877def showmapvme(map, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order=False, show_rb_tree=False):
fe8ab488
A
2878 rsize = 0
2879 if map.pmap != 0:
2880 rsize = int(map.pmap.stats.resident_count)
3e170ce0 2881 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
d9a64523
A
2882 print "{: <#018x} {: <#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end)
2883 showmaphdrvme(map.hdr, map.pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
2884
2885def showmapcopyvme(mapcopy, start_vaddr=0, end_vaddr=0, show_pager_info=True, show_all_shadows=True, reverse_order=False, show_rb_tree=False):
2886 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map_copy","pmap","size","#ents","rsize","start","end")
2887 print "{: <#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(mapcopy,0,0,mapcopy.c_u.hdr.nentries,0,mapcopy.c_u.hdr.links.start,mapcopy.c_u.hdr.links.end)
2888 showmaphdrvme(mapcopy.c_u.hdr, 0, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree)
2889
2890def showmaphdrvme(maphdr, pmap, start_vaddr, end_vaddr, show_pager_info, show_all_shadows, reverse_order, show_rb_tree):
2891 page_size = kern.globals.page_size
2892 vnode_pager_ops = kern.globals.vnode_pager_ops
2893 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
2894 if hasattr(kern.globals, 'compressor_object'):
2895 compressor_object = kern.globals.compressor_object
2896 else:
2897 compressor_object = -1;
2898 vme_list_head = maphdr.links
fe8ab488 2899 vme_ptr_type = GetType('vm_map_entry *')
d9a64523
A
2900 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<16s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
2901 last_end = unsigned(maphdr.links.start)
2902 skipped_entries = 0
2903 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links", reverse_order):
2904 if start_vaddr != 0 and end_vaddr != 0:
2905 if unsigned(vme.links.start) > end_vaddr:
2906 break
2907 if unsigned(vme.links.end) <= start_vaddr:
2908 last_end = unsigned(vme.links.end)
2909 skipped_entries = skipped_entries + 1
2910 continue
2911 if skipped_entries != 0:
2912 print "... skipped {:d} entries ...".format(skipped_entries)
2913 skipped_entries = 0
3e170ce0
A
2914 if unsigned(vme.links.start) != last_end:
2915 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size)
2916 last_end = unsigned(vme.links.end)
2917 size = unsigned(vme.links.end) - unsigned(vme.links.start)
2918 object = vme.vme_object.vmo_object
2919 if object == 0:
d9a64523 2920 object_str = "{: <#018x}".format(object)
3e170ce0
A
2921 elif vme.is_sub_map:
2922 if object == kern.globals.bufferhdr_map:
2923 object_str = "BUFFERHDR_MAP"
2924 elif object == kern.globals.mb_map:
2925 object_str = "MB_MAP"
2926 elif object == kern.globals.bsd_pageable_map:
2927 object_str = "BSD_PAGEABLE_MAP"
2928 elif object == kern.globals.ipc_kernel_map:
2929 object_str = "IPC_KERNEL_MAP"
2930 elif object == kern.globals.ipc_kernel_copy_map:
2931 object_str = "IPC_KERNEL_COPY_MAP"
2932 elif object == kern.globals.kalloc_map:
2933 object_str = "KALLOC_MAP"
2934 elif object == kern.globals.zone_map:
2935 object_str = "ZONE_MAP"
39037602
A
2936 elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map:
2937 object_str = "COMPRESSOR_MAP"
3e170ce0
A
2938 elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map:
2939 object_str = "GZALLOC_MAP"
2940 elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map:
2941 object_str = "G_KEXT_MAP"
2942 elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap:
2943 object_str = "VECTOR_UPL_SUBMAP"
2944 else:
d9a64523 2945 object_str = "submap:{: <#018x}".format(object)
3e170ce0
A
2946 else:
2947 if object == kern.globals.kernel_object:
2948 object_str = "KERNEL_OBJECT"
2949 elif object == kern.globals.vm_submap_object:
2950 object_str = "VM_SUBMAP_OBJECT"
d9a64523 2951 elif object == compressor_object:
3e170ce0
A
2952 object_str = "COMPRESSOR_OBJECT"
2953 else:
d9a64523 2954 object_str = "{: <#018x}".format(object)
3e170ce0
A
2955 offset = unsigned(vme.vme_offset) & ~0xFFF
2956 tag = unsigned(vme.vme_offset & 0xFFF)
d9a64523
A
2957 protection = ""
2958 if vme.protection & 0x1:
2959 protection +="r"
2960 else:
2961 protection += "-"
2962 if vme.protection & 0x2:
2963 protection += "w"
2964 else:
2965 protection += "-"
2966 if vme.protection & 0x4:
2967 protection += "x"
2968 else:
2969 protection += "-"
2970 max_protection = ""
2971 if vme.max_protection & 0x1:
2972 max_protection +="r"
2973 else:
2974 max_protection += "-"
2975 if vme.max_protection & 0x2:
2976 max_protection += "w"
2977 else:
2978 max_protection += "-"
2979 if vme.max_protection & 0x4:
2980 max_protection += "x"
2981 else:
2982 max_protection += "-"
fe8ab488
A
2983 vme_flags = ""
2984 if vme.is_sub_map:
2985 vme_flags += "s"
3e170ce0
A
2986 if vme.needs_copy:
2987 vme_flags += "n"
d9a64523 2988 if vme.use_pmap:
3e170ce0 2989 vme_flags += "p"
d9a64523
A
2990 if vme.wired_count:
2991 vme_flags += "w"
2992 if vme.used_for_jit:
2993 vme_flags += "j"
3e170ce0 2994 tagstr = ""
d9a64523 2995 if pmap == kern.globals.kernel_pmap:
3e170ce0 2996 xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
5ba3f43e 2997 if xsite and xsite.site.flags & 0x0200:
3e170ce0 2998 tagstr = ".{:<3d}".format(xsite.loadTag)
d9a64523
A
2999 rb_info = ""
3000 if show_rb_tree:
3001 rb_info = "l={: <#018x} r={: <#018x} p={: <#018x}".format(vme.store.entry.rbe_left, vme.store.entry.rbe_right, vme.store.entry.rbe_parent)
3002 print "{: <#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:3s}/{:3s}/{:<8s} {:<18s} {:<#18x} {:s}".format(vme,vme.links.start,vme.links.end,(unsigned(vme.links.end)-unsigned(vme.links.start))/page_size,tag,tagstr,protection,max_protection,vme_flags,object_str,offset, rb_info)
3e170ce0
A
3003 if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0:
3004 object = vme.vme_object.vmo_object
fe8ab488
A
3005 else:
3006 object = 0
d9a64523
A
3007 showvmobject(object, offset, size, show_pager_info, show_all_shadows)
3008 if start_vaddr != 0 or end_vaddr != 0:
3009 print "..."
3010 elif unsigned(maphdr.links.end) > last_end:
3011 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,maphdr.links.end,(unsigned(maphdr.links.end) - last_end)/page_size)
fe8ab488
A
3012 return None
3013
3e170ce0
A
3014def CountMapTags(map, tagcounts, slow):
3015 page_size = unsigned(kern.globals.page_size)
3016 vme_list_head = map.hdr.links
3017 vme_ptr_type = GetType('vm_map_entry *')
3018 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
3019 object = vme.vme_object.vmo_object
3020 tag = vme.vme_offset & 0xFFF
3021 if object == kern.globals.kernel_object:
3022 count = 0
3023 if not slow:
3024 count = unsigned(vme.links.end - vme.links.start) / page_size
3025 else:
3026 addr = unsigned(vme.links.start)
3027 while addr < unsigned(vme.links.end):
3028 hash_id = _calc_vm_page_hash(object, addr)
3029 page_list = kern.globals.vm_page_buckets[hash_id].page_list
3030 page = _vm_page_unpack_ptr(page_list)
3031 while (page != 0):
3032 vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
d9a64523
A
3033 if (addr == unsigned(vmpage.vmp_offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vmp_object))):
3034 if (not vmpage.vmp_local) and (vmpage.vmp_wire_count > 0):
3e170ce0
A
3035 count += 1
3036 break
d9a64523 3037 page = _vm_page_unpack_ptr(vmpage.vmp_next_m)
3e170ce0
A
3038 addr += page_size
3039 tagcounts[tag] += count
3040 elif vme.is_sub_map:
3041 CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
3042 return None
3043
3044def CountWiredObject(object, tagcounts):
3045 tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
3046 return None
3047
3e170ce0
A
3048def GetKmodIDName(kmod_id):
3049 kmod_val = kern.globals.kmod
3050 for kmod in IterateLinkedList(kmod_val, 'next'):
3051 if (kmod.id == kmod_id):
3052 return "{:<50s}".format(kmod.name)
3053 return "??"
3054
5ba3f43e
A
3055FixedTags = {
3056 0: "VM_KERN_MEMORY_NONE",
3057 1: "VM_KERN_MEMORY_OSFMK",
3058 2: "VM_KERN_MEMORY_BSD",
3059 3: "VM_KERN_MEMORY_IOKIT",
3060 4: "VM_KERN_MEMORY_LIBKERN",
3061 5: "VM_KERN_MEMORY_OSKEXT",
3062 6: "VM_KERN_MEMORY_KEXT",
3063 7: "VM_KERN_MEMORY_IPC",
3064 8: "VM_KERN_MEMORY_STACK",
3065 9: "VM_KERN_MEMORY_CPU",
3066 10: "VM_KERN_MEMORY_PMAP",
3067 11: "VM_KERN_MEMORY_PTE",
3068 12: "VM_KERN_MEMORY_ZONE",
3069 13: "VM_KERN_MEMORY_KALLOC",
3070 14: "VM_KERN_MEMORY_COMPRESSOR",
3071 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
3072 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
3073 17: "VM_KERN_MEMORY_WAITQ",
3074 18: "VM_KERN_MEMORY_DIAG",
3075 19: "VM_KERN_MEMORY_LOG",
3076 20: "VM_KERN_MEMORY_FILE",
3077 21: "VM_KERN_MEMORY_MBUF",
3078 22: "VM_KERN_MEMORY_UBC",
3079 23: "VM_KERN_MEMORY_SECURITY",
3080 24: "VM_KERN_MEMORY_MLOCK",
3081 25: "VM_KERN_MEMORY_REASON",
3082 26: "VM_KERN_MEMORY_SKYWALK",
3083 27: "VM_KERN_MEMORY_LTABLE",
3084 255:"VM_KERN_MEMORY_ANY",
3085}
3e170ce0 3086
5ba3f43e 3087def GetVMKernName(tag):
cb323159
A
3088 """ returns the formatted name for a vmtag and
3089 the sub-tag for kmod tags.
3090 """
3091 if ((tag <= 27) or (tag == 255)):
3092 return (FixedTags[tag], "")
3093 site = kern.globals.vm_allocation_sites[tag]
3094 if site:
3095 if site.flags & 0x007F:
3096 cstr = addressof(site.subtotals[site.subtotalscount])
3097 return ("{:<50s}".format(str(Cast(cstr, 'char *'))), "")
3098 else:
3099 if site.flags & 0x0200:
3100 xsite = Cast(site,'OSKextAccount *')
3101 tagstr = ".{:<3d}".format(xsite.loadTag)
3102 return (GetKmodIDName(xsite.loadTag), tagstr);
3103 else:
3104 return (kern.Symbolicate(site), "")
3105 return ("", "")
3e170ce0 3106
d9a64523 3107@lldb_command("showvmtags", "AS")
3e170ce0
A
3108def showvmtags(cmd_args=None, cmd_options={}):
3109 """Routine to print out info about kernel wired page allocations
3110 usage: showvmtags
3111 iterates kernel map and vm objects totaling allocations by tag.
3112 usage: showvmtags -S
3113 also iterates kernel object pages individually - slow.
d9a64523
A
3114 usage: showvmtags -A
3115 show all tags, even tags that have no wired count
3e170ce0
A
3116 """
3117 slow = False
3118 if "-S" in cmd_options:
3119 slow = True
d9a64523
A
3120 all_tags = False
3121 if "-A" in cmd_options:
3122 all_tags = True
3e170ce0 3123 page_size = unsigned(kern.globals.page_size)
94ff46dc 3124 nsites = unsigned(kern.globals.vm_allocation_tag_highest) + 1
cb323159
A
3125 tagcounts = [0] * nsites
3126 tagpeaks = [0] * nsites
3127 tagmapped = [0] * nsites
5ba3f43e
A
3128
3129 if kern.globals.vm_tag_active_update:
cb323159 3130 for tag in range(nsites):
5ba3f43e
A
3131 site = kern.globals.vm_allocation_sites[tag]
3132 if site:
cb323159
A
3133 tagcounts[tag] = unsigned(site.total)
3134 tagmapped[tag] = unsigned(site.mapped)
3135 tagpeaks[tag] = unsigned(site.peak)
5ba3f43e
A
3136 else:
3137 queue_head = kern.globals.vm_objects_wired
d9a64523 3138 for object in IterateQueue(queue_head, 'struct vm_object *', 'wired_objq'):
5ba3f43e
A
3139 if object != kern.globals.kernel_object:
3140 CountWiredObject(object, tagcounts)
3e170ce0 3141
5ba3f43e 3142 CountMapTags(kern.globals.kernel_map, tagcounts, slow)
3e170ce0
A
3143
3144 total = 0
cb323159 3145 totalmapped = 0
94ff46dc 3146 print " vm_allocation_tag_highest: {:<7d} ".format(nsites - 1)
cb323159
A
3147 print " {:<7s} {:>7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod", "peak", "size", "mapped", "name")
3148 for tag in range(nsites):
3149 if all_tags or tagcounts[tag] or tagmapped[tag]:
3e170ce0 3150 total += tagcounts[tag]
cb323159
A
3151 totalmapped += tagmapped[tag]
3152 (sitestr, tagstr) = GetVMKernName(tag)
3153 site = kern.globals.vm_allocation_sites[tag]
3154 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:>7d}K {:<50s}".format(tag, tagstr, tagpeaks[tag] / 1024, tagcounts[tag] / 1024, tagmapped[tag] / 1024, sitestr)
3155
3156 for sub in range(site.subtotalscount):
3157 alloctag = unsigned(site.subtotals[sub].tag)
3158 amount = unsigned(site.subtotals[sub].total)
3159 subsite = kern.globals.vm_allocation_sites[alloctag]
3160 if alloctag and subsite:
3161 if ((subsite.flags & 0x007f) == 0):
3162 kind_str = "named"
3e170ce0 3163 else:
cb323159
A
3164 kind_str = "from"
3165 (sitestr, tagstr) = GetVMKernName(alloctag)
3166 print " {:>7s} {:>7s} {:>7s} {:>7d}K {:s} {:>3d}{:<4s} {:<50s}".format(" ", " ", " ", amount / 1024, kind_str, alloctag, tagstr, sitestr)
3167
3168 print "Total: {:>7d}K {:>7d}K".format(total / 1024, totalmapped / 1024)
3e170ce0
A
3169 return None
3170
3171
fe8ab488 3172def FindVMEntriesForVnode(task, vn):
5ba3f43e 3173 """ returns an array of vme that have the vnode set to defined vnode
fe8ab488
A
3174 each entry in array is of format (vme, start_addr, end_address, protection)
3175 """
3176 retval = []
3177 vmmap = task.map
3178 pmap = vmmap.pmap
3179 pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
3180 debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
3181
3182 if unsigned(pmap) == 0:
3183 return retval
3184 vme_list_head = vmmap.hdr.links
3185 vme_ptr_type = gettype('vm_map_entry *')
3186 for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
3187 #print vme
3e170ce0
A
3188 if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0:
3189 obj = vme.vme_object.vmo_object
fe8ab488
A
3190 else:
3191 continue
3192
3193 while obj != 0:
3194 if obj.pager != 0:
3195 if obj.internal:
3196 pass
3197 else:
3198 vn_pager = Cast(obj.pager, 'vnode_pager *')
5ba3f43e 3199 if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
fe8ab488
A
3200 retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
3201 obj = obj.shadow
3202 return retval
3203
3204@lldb_command('showtaskloadinfo')
3205def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
3206 """ Print the load address and uuid for the process
3207 Usage: (lldb)showtaskloadinfo <task_t>
3208 """
3209 if not cmd_args:
3210 raise ArgumentError("Insufficient arguments")
3211 t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
3212 print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
3213 p = Cast(t.bsd_info, 'struct proc *')
3214 uuid = p.p_uuid
3215 uuid_out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=uuid)
3216 filepath = GetVnodePath(p.p_textvp)
3217 libname = filepath.split('/')[-1]
3218 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
3219 mappings = FindVMEntriesForVnode(t, p.p_textvp)
3220 load_addr = 0
3221 end_addr = 0
3222 for m in mappings:
3223 if m[3] == 5:
3224 load_addr = m[1]
3225 end_addr = m[2]
3226 #print "Load address: %s" % hex(m[1])
3227 print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
3e170ce0
A
3228 return None
3229
3230@header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3231@lldb_command('vmpagelookup')
3232def VMPageLookup(cmd_args=None):
3233 """ Print the pages in the page bucket corresponding to the provided object and offset.
3234 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3235 """
3236 if cmd_args == None or len(cmd_args) < 2:
3237 raise ArgumentError("Please specify an object and offset.")
3238 format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3239
3240 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3241 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3242
3243 hash_id = _calc_vm_page_hash(obj, off)
3244
3245 page_list = kern.globals.vm_page_buckets[hash_id].page_list
3246 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
3247
3248 print VMPageLookup.header
3249 page = _vm_page_unpack_ptr(page_list)
3250 while (page != 0) :
3251 pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
d9a64523
A
3252 print format_string.format(page, pg_t.vmp_offset, _vm_page_unpack_ptr(pg_t.vmp_object))
3253 page = _vm_page_unpack_ptr(pg_t.vmp_next_m)
3e170ce0 3254
39037602
A
3255
3256
3257@lldb_command('vmpage_get_phys_page')
3258def VmPageGetPhysPage(cmd_args=None):
3259 """ return the physical page for a vm_page_t
3260 usage: vm_page_get_phys_page <vm_page_t>
3261 """
3262 if cmd_args == None or len(cmd_args) < 1:
3263 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
3264 return
3265
3266 page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t')
3267 phys_page = _vm_page_get_phys_page(page)
3268 print("phys_page = 0x%x\n" % phys_page)
3269
3270
3271def _vm_page_get_phys_page(page):
3272 if kern.arch == 'x86_64':
d9a64523 3273 return page.vmp_phys_page
39037602
A
3274
3275 if page == 0 :
3276 return 0
3277
3278 m = unsigned(page)
3279 if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) :
3280 return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum)
3281
3282 page_with_ppnum = Cast(page, 'uint32_t *')
3283 ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t')
3284 return page_with_ppnum[ppnum_offset]
3285
3286
3287@lldb_command('vmpage_unpack_ptr')
3288def VmPageUnpackPtr(cmd_args=None):
3289 """ unpack a pointer
3290 usage: vm_page_unpack_ptr <packed_ptr>
3291 """
3292 if cmd_args == None or len(cmd_args) < 1:
3293 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
3294 return
3295
3296 packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
3297 unpacked = _vm_page_unpack_ptr(packed)
3298 print("unpacked pointer = 0x%x\n" % unpacked)
3299
3300
3e170ce0
A
3301def _vm_page_unpack_ptr(page):
3302 if kern.ptrsize == 4 :
3303 return page
3304
3305 if page == 0 :
3306 return page
3307
3308 min_addr = kern.globals.vm_min_kernel_and_kext_address
39037602
A
3309 ptr_shift = kern.globals.vm_packed_pointer_shift
3310 ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
3e170ce0
A
3311 #INTEL - min_addr = 0xffffff7f80000000
3312 #ARM - min_addr = 0x80000000
3313 #ARM64 - min_addr = 0xffffff8000000000
39037602
A
3314 if unsigned(page) & unsigned(ptr_mask) :
3315 masked_page = (unsigned(page) & ~ptr_mask)
0a7de745
A
3316 # can't use addressof(kern.globals.vm_pages[masked_page]) due to 32 bit limitation in SB bridge
3317 vm_pages_addr = unsigned(addressof(kern.globals.vm_pages[0]))
3318 element_size = unsigned(addressof(kern.globals.vm_pages[1])) - vm_pages_addr
3319 return (vm_pages_addr + masked_page * element_size)
39037602 3320 return ((unsigned(page) << unsigned(ptr_shift)) + unsigned(min_addr))
3e170ce0
A
3321
3322@lldb_command('calcvmpagehash')
3323def CalcVMPageHash(cmd_args=None):
3324 """ Get the page bucket corresponding to the provided object and offset.
3325 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3326 """
3327 if cmd_args == None or len(cmd_args) < 2:
3328 raise ArgumentError("Please specify an object and offset.")
3329
3330 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3331 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3332
3333 hash_id = _calc_vm_page_hash(obj, off)
3334
3335 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
3336 return None
3337
3338def _calc_vm_page_hash(obj, off):
3339 bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
3340 hash_mask = (int) (kern.globals.vm_page_hash_mask)
3341
3342 one = (obj * bucket_hash) & 0xFFFFFFFF
3343 two = off >> unsigned(kern.globals.page_shift)
3344 three = two ^ bucket_hash
3345 four = one + three
3346 hash_id = four & hash_mask
3347
3348 return hash_id
3349
cb323159
A
3350def AddressIsFromZoneMap(addr):
3351 zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
3352 zone_map_max_address = kern.GetGlobalVariable('zone_map_max_address')
3353 if (unsigned(addr) >= unsigned(zone_map_min_address)) and (unsigned(addr) < unsigned(zone_map_max_address)):
3354 return 1
3355 else:
3356 return 0
3357
3358def ElementOffsetInForeignPage():
3359 zone_element_alignment = 32 # defined in zalloc.c
3360 zone_page_metadata_size = sizeof('struct zone_page_metadata')
3361 if zone_page_metadata_size % zone_element_alignment == 0:
3362 offset = zone_page_metadata_size
3363 else:
3364 offset = zone_page_metadata_size + (zone_element_alignment - (zone_page_metadata_size % zone_element_alignment))
3365 return unsigned(offset)
3366
3367def ElementStartAddrFromZonePageMetadata(page_metadata):
3368 zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
3369 zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
3370 page_size = kern.GetGlobalVariable('page_size')
3371 if AddressIsFromZoneMap(page_metadata):
3372 page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
3373 element_start_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
3374 else:
3375 element_start_addr = unsigned(page_metadata) + unsigned(ElementOffsetInForeignPage())
3376
3377 return element_start_addr
3378
3379def ZonePageStartAddrFromZonePageMetadata(page_metadata):
3380 zone_metadata_region_min = kern.GetGlobalVariable('zone_metadata_region_min')
3381 zone_map_min_address = kern.GetGlobalVariable('zone_map_min_address')
3382 page_size = kern.GetGlobalVariable('page_size')
3383
3384 if AddressIsFromZoneMap(page_metadata):
3385 page_index = (unsigned(page_metadata) - unsigned(zone_metadata_region_min)) / sizeof('struct zone_page_metadata')
3386 zone_page_addr = unsigned(zone_map_min_address) + unsigned(page_index * page_size)
3387 else:
3388 zone_page_addr = unsigned(page_metadata)
3389
3390 return unsigned(zone_page_addr)
3391
3392def CreateFreeElementsList(zone, first_free):
3393 free_elements = []
3394 if unsigned(first_free) == 0:
3395 return free_elements
3396 current = first_free
3397 while True:
3398 free_elements.append(unsigned(current))
3399 next = dereference(Cast(current, 'vm_offset_t *'))
3400 next = (unsigned(next) ^ unsigned(kern.globals.zp_nopoison_cookie))
3401 next = kern.GetValueFromAddress(next, 'vm_offset_t *')
3402 if unsigned(next) == 0:
3403 break;
3404 current = Cast(next, 'void *')
3405
3406 return free_elements
3407
3408#Macro: showallocatedzoneelement
3409@lldb_command('showallocatedzoneelement')
3410def ShowAllocatedElementsInZone(cmd_args=None, cmd_options={}):
3411 """ Show all the allocated elements in a zone
3412 usage: showzoneallocelements <address of zone>
3413 """
3414 if len(cmd_args) < 1:
3415 raise ArgumentError("Please specify a zone")
3416
3417 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
3418 elements = FindAllocatedElementsInZone(zone)
3419 i = 1
3420 for elem in elements:
3421 print "{0: >10d}/{1:<10d} element: {2: <#20x}".format(i, len(elements), elem)
3422 i += 1
3423
3424#EndMacro: showallocatedzoneelement
3425
3426def FindAllocatedElementsInZone(zone):
3427 page_size = kern.GetGlobalVariable('page_size')
3428 elements = []
3429 page_queues = ["any_free_foreign", "intermediate", "all_used"]
3430 found_total = 0
3431
3432 for queue in page_queues:
3433 found_in_queue = 0
3434 if queue == "any_free_foreign" and unsigned(zone.allows_foreign) != 1:
3435 continue
3436
3437 for zone_page_metadata in IterateQueue(zone.pages.__getattr__(queue), 'struct zone_page_metadata *', 'pages'):
3438 free_elements = []
3439 first_free_element = kern.GetValueFromAddress(GetFreeList(zone_page_metadata))
3440 free_elements = CreateFreeElementsList(zone, first_free_element)
3441
3442 chunk_page_count = zone_page_metadata.page_count
3443 element_addr_start = ElementStartAddrFromZonePageMetadata(zone_page_metadata)
3444 zone_page_start = ZonePageStartAddrFromZonePageMetadata(zone_page_metadata)
3445 next_page = zone_page_start + page_size
3446 element_addr_end = zone_page_start + (chunk_page_count * page_size)
3447 elem = unsigned(element_addr_start)
3448 while elem < element_addr_end:
3449 if elem not in free_elements:
3450 elements.append(elem)
3451 found_in_queue += 1
3452 elem += zone.elem_size
3453
3454 if queue == "any_free_foreign":
3455 if (elem + zone.elem_size) >= next_page:
3456 zone_page_start = unsigned((elem + page_size) & ~(page_size - 1))
3457 next_page = zone_page_start + page_size
3458 elem = zone_page_start + unsigned(ElementOffsetInForeignPage())
3459
3460 found_total += found_in_queue
3461# print "Found {0: <d} allocated elements in the {1: <s} page queue".format(found_in_queue, queue)
3462
3463# print "Total number of allocated elements: {0: <d} in zone {1: <s}".format(found_total, zone.zone_name)
3464 return elements
3465
3466def match_vm_page_attributes(page, matching_attributes):
3467 page_ptr = addressof(page)
3468 unpacked_vm_object = _vm_page_unpack_ptr(page.vmp_object)
3469 matched_attributes = 0
3470 if "vmp_q_state" in matching_attributes and (page.vmp_q_state == matching_attributes["vmp_q_state"]):
3471 matched_attributes += 1
3472 if "vm_object" in matching_attributes and (unsigned(unpacked_vm_object) == unsigned(matching_attributes["vm_object"])):
3473 matched_attributes += 1
3474 if "vmp_offset" in matching_attributes and (unsigned(page.vmp_offset) == unsigned(matching_attributes["vmp_offset"])):
3475 matched_attributes += 1
3476 if "phys_page" in matching_attributes and (unsigned(_vm_page_get_phys_page(page_ptr)) == unsigned(matching_attributes["phys_page"])):
3477 matched_attributes += 1
3478 if "bitfield" in matching_attributes and unsigned(page.__getattr__(matching_attributes["bitfield"])) == 1:
3479 matched_attributes += 1
3480
3481 return matched_attributes
3482
3483#Macro scan_vm_pages
3484@header("{0: >26s}{1: >20s}{2: >10s}{3: >20s}{4: >20s}{5: >16s}".format("vm_pages_index/zone", "vm_page", "q_state", "vm_object", "offset", "ppn", "bitfield", "from_zone_map"))
3485@lldb_command('scan_vm_pages', 'S:O:F:I:P:B:I:N:ZA')
3486def ScanVMPages(cmd_args=None, cmd_options={}):
3487 """ Scan the global vm_pages array (-A) and/or vmpages zone (-Z) for pages with matching attributes.
3488 usage: scan_vm_pages <matching attribute(s)> [-A start vm_pages index] [-N number of pages to scan] [-Z scan vm_pages zone]
3489
3490 scan_vm_pages -A: scan vm pages in the global vm_pages array
3491 scan_vm_pages -Z: scan vm pages allocated from the vm.pages zone
3492 scan_vm_pages <-A/-Z> -S <vm_page_q_state value>: Find vm pages in the specified queue
3493 scan_vm_pages <-A/-Z> -O <vm_object>: Find vm pages in the specified vm_object
3494 scan_vm_pages <-A/-Z> -F <offset>: Find vm pages with the specified vmp_offset value
3495 scan_vm_pages <-A/-Z> -P <phys_page>: Find vm pages with the specified physical page number
3496 scan_vm_pages <-A/-Z> -B <bitfield>: Find vm pages with the bitfield set
3497 scan_vm_pages <-A> -I <start_index>: Start the scan from start_index
3498 scan_vm_pages <-A> -N <npages>: Scan at most npages
3499 """
3500 if (len(cmd_options) < 1):
3501 raise ArgumentError("Please specify at least one matching attribute")
3502
3503 vm_pages = kern.globals.vm_pages
3504 vm_pages_count = kern.globals.vm_pages_count
3505
3506 start_index = 0
3507 npages = vm_pages_count
3508 scan_vmpages_array = False
3509 scan_vmpages_zone = False
3510 attribute_count = 0
3511
3512 if "-A" in cmd_options:
3513 scan_vmpages_array = True
3514
3515 if "-Z" in cmd_options:
3516 scan_vmpages_zone = True
3517
3518 if scan_vmpages_array == False and scan_vmpages_zone == False:
3519 raise ArgumentError("Please specify where to scan (-A: vm_pages array, -Z: vm.pages zone)")
3520
3521 attribute_values = {}
3522 if "-S" in cmd_options:
3523 attribute_values["vmp_q_state"] = kern.GetValueFromAddress(cmd_options["-S"], 'int')
3524 attribute_count += 1
3525
3526 if "-O" in cmd_options:
3527 attribute_values["vm_object"] = kern.GetValueFromAddress(cmd_options["-O"], 'vm_object_t')
3528 attribute_count += 1
3529
3530 if "-F" in cmd_options:
3531 attribute_values["vmp_offset"] = kern.GetValueFromAddress(cmd_options["-F"], 'unsigned long long')
3532 attribute_count += 1
3533
3534 if "-P" in cmd_options:
3535 attribute_values["phys_page"] = kern.GetValueFromAddress(cmd_options["-P"], 'unsigned int')
3536 attribute_count += 1
3537
3538 if "-B" in cmd_options:
3539 valid_vmp_bitfields = [
3540 "vmp_in_background",
3541 "vmp_on_backgroundq",
3542 "vmp_gobbled",
3543 "vmp_laundry",
3544 "vmp_no_cache",
3545 "vmp_private",
3546 "vmp_reference",
3547 "vmp_busy",
3548 "vmp_wanted",
3549 "vmp_tabled",
3550 "vmp_hashed",
3551 "vmp_fictitious",
3552 "vmp_clustered",
3553 "vmp_pmapped",
3554 "vmp_xpmapped",
3555 "vmp_free_when_done",
3556 "vmp_absent",
3557 "vmp_error",
3558 "vmp_dirty",
3559 "vmp_cleaning",
3560 "vmp_precious",
3561 "vmp_overwriting",
3562 "vmp_restart",
3563 "vmp_unusual",
3564 "vmp_cs_validated",
3565 "vmp_cs_tainted",
3566 "vmp_cs_nx",
3567 "vmp_reusable",
3568 "vmp_lopage",
3569 "vmp_written_by_kernel",
3570 "vmp_unused_object_bits"
3571 ]
3572 attribute_values["bitfield"] = cmd_options["-B"]
3573 if attribute_values["bitfield"] in valid_vmp_bitfields:
3574 attribute_count += 1
3575 else:
3576 raise ArgumentError("Unknown bitfield: {0:>20s}".format(bitfield))
3577
3578 if "-I" in cmd_options:
3579 start_index = kern.GetValueFromAddress(cmd_options["-I"], 'int')
3580 npages = vm_pages_count - start_index
3581
3582 if "-N" in cmd_options:
3583 npages = kern.GetValueFromAddress(cmd_options["-N"], 'int')
3584 if npages == 0:
3585 raise ArgumentError("You specified -N 0, nothing to be scanned")
3586
3587 end_index = start_index + npages - 1
3588 if end_index >= vm_pages_count:
3589 raise ArgumentError("Index range out of bound. vm_pages_count: {0:d}".format(vm_pages_count))
3590
3591 header_after_n_lines = 40
3592 format_string = "{0: >26s}{1: >#20x}{2: >10d}{3: >#20x}{4: >#20x}{5: >#16x}"
3593
3594 found_in_array = 0
3595 if scan_vmpages_array:
3596 print "Scanning vm_pages[{0:d} to {1:d}] for {2:d} matching attribute(s)......".format(start_index, end_index, attribute_count)
3597 i = start_index
3598 while i <= end_index:
3599 page = vm_pages[i]
3600 if match_vm_page_attributes(page, attribute_values) == attribute_count:
3601 if found_in_array % header_after_n_lines == 0:
3602 print ScanVMPages.header
3603
3604 print format_string.format(str(i), addressof(page), page.vmp_q_state, _vm_page_unpack_ptr(page.vmp_object), page.vmp_offset, _vm_page_get_phys_page(addressof(page)))
3605 found_in_array += 1
3606
3607 i += 1
3608
3609 found_in_zone = 0
3610 if scan_vmpages_zone:
3611 page_size = kern.GetGlobalVariable('page_size')
3612 num_zones = kern.GetGlobalVariable('num_zones')
3613 zone_array = kern.GetGlobalVariable('zone_array')
3614 print "Scanning vm.pages zone for {0:d} matching attribute(s)......".format(attribute_count)
3615 i = 0
3616 while i < num_zones:
3617 zone = zone_array[i]
3618 if str(zone.zone_name) == "vm pages":
3619 break;
3620 i += 1
3621
3622 if i == num_zones:
3623 print "Cannot find vm_pages zone, skip the scan"
3624 else:
3625 print "Scanning page queues in the vm_pages zone..."
3626 elements = FindAllocatedElementsInZone(zone)
3627 for elem in elements:
3628 page = kern.GetValueFromAddress(elem, 'vm_page_t')
3629
3630 if match_vm_page_attributes(page, attribute_values) == attribute_count:
3631 if found_in_zone % header_after_n_lines == 0:
3632 print ScanVMPages.header
3633
3634 vm_object = _vm_page_unpack_ptr(page.vmp_object)
3635 phys_page = _vm_page_get_phys_page(page)
3636 print format_string.format("vm_pages zone", elem, page.vmp_q_state, vm_object, page.vmp_offset, phys_page)
3637 found_in_zone += 1
3638
3639 total = found_in_array + found_in_zone
3640 print "Found {0:d} vm pages ({1:d} in array, {2:d} in zone) matching the requested {3:d} attribute(s)".format(total, found_in_array, found_in_zone, attribute_count)
3641
3642#EndMacro scan_vm_pages
3643
39037602
A
3644VM_PAGE_IS_WIRED = 1
3645
3e170ce0 3646@header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield"))
cb323159 3647@lldb_command('vmobjectwalkpages', 'CSBNQP:O:')
3e170ce0
A
3648def VMObjectWalkPages(cmd_args=None, cmd_options={}):
3649 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
3650 specifically look for this page, highlighting it in the output or noting if it was not found. For
3651 each page, we confirm that it points to the object. We also keep track of the number of pages we
3652 see and compare this to the object's resident page count field.
3653 Usage:
3654 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
cb323159 3655 vmobjectwalkpages <vm_object_t> -C : list pages in compressor after processing resident pages
3e170ce0
A
3656 vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards
3657 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
39037602 3658 vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. q_state == VM_PAGE_IS_WIRED && wire_count == 0)
3e170ce0
A
3659 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
3660 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
cb323159 3661 vmobjectwalkpages <vm_object_t> -O <offset> : Like -P, but looks for given offset
3e170ce0
A
3662
3663 """
3664
3665 if (cmd_args == None or len(cmd_args) < 1):
3666 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
3667
3668 out_string = ""
3669
3670 obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3671
3672 page = 0
3673 if "-P" in cmd_options:
3674 page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
3675
cb323159
A
3676 off = -1
3677 if "-O" in cmd_options:
3678 off = kern.GetValueFromAddress(cmd_options['-O'], 'vm_offset_t')
3679
3e170ce0
A
3680 stop = 0
3681 if "-S" in cmd_options:
cb323159
A
3682 if page == 0 and off < 0:
3683 raise ArgumentError("-S can only be passed when a page is specified with -P or -O")
3e170ce0
A
3684 stop = 1
3685
3686 walk_backwards = False
3687 if "-B" in cmd_options:
3688 walk_backwards = True
3689
3690 quiet_mode = False
3691 if "-Q" in cmd_options:
3692 quiet_mode = True
3693
3694 if not quiet_mode:
3695 print VMObjectWalkPages.header
3696 format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
39037602
A
3697 first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
3698 second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
3699 second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
3700 second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
3e170ce0 3701 second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
3e170ce0
A
3702
3703 limit = 4096 #arbitrary limit of number of pages to walk
3704 ignore_limit = 0
3705 if "-N" in cmd_options:
3706 ignore_limit = 1
3707
cb323159
A
3708 show_compressed = 0
3709 if "-C" in cmd_options:
3710 show_compressed = 1
3711
3e170ce0
A
3712 page_count = 0
3713 res_page_count = unsigned(obj.resident_page_count)
3714 page_found = False
3715 pages_seen = set()
3716
d9a64523 3717 for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
3e170ce0
A
3718 page_count += 1
3719 out_string = ""
3720 if (page != 0 and not(page_found) and vmp == page):
3721 out_string += "******"
3722 page_found = True
3723
cb323159
A
3724 if (off > 0 and not(page_found) and vmp.vmp_offset == off):
3725 out_string += "******"
3726 page_found = True
3727
3728 if page != 0 or off > 0 or quiet_mode:
3e170ce0
A
3729 if (page_count % 1000) == 0:
3730 print "traversed %d pages ...\n" % (page_count)
3731 else:
0a7de745 3732 out_string += format_string.format(page_count, res_page_count, vmp, vmp.vmp_offset, _vm_page_unpack_ptr(vmp.vmp_listq.next), _vm_page_get_phys_page(vmp), vmp.vmp_wire_count)
d9a64523
A
3733 out_string += first_bitfield_format_string.format(vmp.vmp_q_state, vmp.vmp_in_background, vmp.vmp_on_backgroundq, vmp.vmp_gobbled, vmp.vmp_laundry, vmp.vmp_no_cache,
3734 vmp.vmp_private, vmp.vmp_reference)
3e170ce0 3735
d9a64523
A
3736 if hasattr(vmp,'slid'):
3737 vmp_slid = vmp.slid
3738 else:
3739 vmp_slid = 0
3740 out_string += second_bitfield_format_string.format(vmp.vmp_busy, vmp.vmp_wanted, vmp.vmp_tabled, vmp.vmp_hashed, vmp.vmp_fictitious, vmp.vmp_clustered,
3741 vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
3742 vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
3743 vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
3744 vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
3745 vmp.vmp_written_by_kernel)
3e170ce0
A
3746
3747 if (vmp in pages_seen):
3748 print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
3749 return
3750
d9a64523
A
3751 if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
3752 print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp.vmp_object)))
3e170ce0
A
3753 return
3754
d9a64523 3755 if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
39037602
A
3756 print out_string + " page in wired state with wire_count of 0\n"
3757 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
3e170ce0
A
3758 print "stopping...\n"
3759 return
3760
d9a64523
A
3761 if ((vmp.vmp_unused_page_bits != 0) or (vmp.vmp_unused_object_bits != 0)):
3762 print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d unused_object_bits : %d\n" % (vmp.vmp_unused_page_bits,
3763 vmp.vmp_unused_object_bits)
3e170ce0
A
3764 print "stopping...\n"
3765 return
3766
3767 pages_seen.add(vmp)
3768
3769 if False:
d9a64523 3770 hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
3e170ce0
A
3771 hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
3772 hash_page = _vm_page_unpack_ptr(hash_page_list)
3773 hash_page_t = 0
3774
3775 while (hash_page != 0):
3776 hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
3777 if hash_page_t == vmp:
3778 break
d9a64523 3779 hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
3e170ce0
A
3780
3781 if (unsigned(vmp) != unsigned(hash_page_t)):
3782 print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
d9a64523 3783 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))
3e170ce0
A
3784 return
3785
3786 if (page_count >= limit and not(ignore_limit)):
3787 print out_string + "Limit reached (%d pages), stopping..." % (limit)
cb323159 3788 break
3e170ce0
A
3789
3790 print out_string
3791
3792 if page_found and stop:
3793 print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count)))
3794 return
3795
3796 if (page != 0):
3797 print("page found? : %s\n" % page_found)
3798
cb323159
A
3799 if (off > 0):
3800 print("page found? : %s\n" % page_found)
3801
3e170ce0
A
3802 print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count)))
3803
cb323159
A
3804 if show_compressed != 0 and obj.pager != 0 and unsigned(obj.pager.mo_pager_ops) == unsigned(addressof(kern.globals.compressor_pager_ops)):
3805 pager = Cast(obj.pager, 'compressor_pager *')
3806 chunks = pager.cpgr_num_slots / 128
3807 pagesize = kern.globals.page_size
3808
3809 page_idx = 0
3810 while page_idx < pager.cpgr_num_slots:
3811 if chunks != 0:
3812 chunk = pager.cpgr_slots.cpgr_islots[page_idx / 128]
3813 slot = chunk[page_idx % 128]
3814 elif pager.cpgr_num_slots > 2:
3815 slot = pager.cpgr_slots.cpgr_dslots[page_idx]
3816 else:
3817 slot = pager.cpgr_slots.cpgr_eslots[page_idx]
3818
3819 if slot != 0:
3820 print("compressed page for offset: %x slot %x\n" % ((page_idx * pagesize) - obj.paging_offset, slot))
3821 page_idx = page_idx + 1
3822
3e170ce0
A
3823
3824@lldb_command("show_all_apple_protect_pagers")
3825def ShowAllAppleProtectPagers(cmd_args=None):
3826 """Routine to print all apple_protect pagers
3827 usage: show_all_apple_protect_pagers
3828 """
3829 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
3830 qhead = kern.globals.apple_protect_pager_queue
3831 qtype = GetType('apple_protect_pager *')
3832 qcnt = kern.globals.apple_protect_pager_count
3833 idx = 0
3834 for pager in IterateQueue(qhead, qtype, "pager_queue"):
3835 idx = idx + 1
3836 show_apple_protect_pager(pager, qcnt, idx)
3837
3838@lldb_command("show_apple_protect_pager")
3839def ShowAppleProtectPager(cmd_args=None):
3840 """Routine to print out info about an apple_protect pager
3841 usage: show_apple_protect_pager <pager>
3842 """
3843 if cmd_args == None or len(cmd_args) < 1:
d9a64523 3844 print "Invalid argument.", ShowAppleProtectPager.__doc__
3e170ce0 3845 return
d9a64523 3846 pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
3e170ce0
A
3847 show_apple_protect_pager(pager, 1, 1)
3848
3849def show_apple_protect_pager(pager, qcnt, idx):
3850 object = pager.backing_object
3851 shadow = object.shadow
3852 while shadow != 0:
3853 object = shadow
3854 shadow = object.shadow
3855 vnode_pager = Cast(object.pager,'vnode_pager *')
3856 filename = GetVnodePath(vnode_pager.vnode_handle)
d9a64523 3857 print "{:>3}/{:<3d} {: <#018x} {:>5d} {:>5d} {:>6d} {: <#018x} {: <#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{: <#018x} <decrypt:{: <#018x} end:{:#018x} ops:{: <#018x} refs:{:<d}>\n\tvnode:{: <#018x} {:s}\n".format(idx, qcnt, pager, pager.ref_count, pager.is_ready, pager.is_mapped, pager.pager_control, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename)
39037602
A
3858
3859@lldb_command("show_console_ring")
3860def ShowConsoleRingData(cmd_args=None):
3861 """ Print console ring buffer stats and data
3862 """
3863 cr = kern.globals.console_ring
3864 print "console_ring = {:#018x} buffer = {:#018x} length = {:<5d} used = {:<5d} read_ptr = {:#018x} write_ptr = {:#018x}".format(addressof(cr), cr.buffer, cr.len, cr.used, cr.read_ptr, cr.write_ptr)
3865 pending_data = []
3866 for i in range(unsigned(cr.used)):
3867 idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
3868 pending_data.append("{:c}".format(cr.buffer[idx]))
3869
3870 if pending_data:
3871 print "Data:"
3872 print "".join(pending_data)
3873
3874# Macro: showjetsamsnapshot
3875
3876@lldb_command("showjetsamsnapshot", "DA")
3877def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
3878 """ Dump entries in the jetsam snapshot table
3879 usage: showjetsamsnapshot [-D] [-A]
3880 Use -D flag to print extra physfootprint details
3881 Use -A flag to print all entries (regardless of valid count)
3882 """
3883
3884 # Not shown are uuid, user_data, cpu_time
3885
3886 global kern
39037602
A
3887
3888 show_footprint_details = False
3889 show_all_entries = False
3890
3891 if "-D" in cmd_options:
3892 show_footprint_details = True
3893
3894 if "-A" in cmd_options:
3895 show_all_entries = True
3896
3897 valid_count = kern.globals.memorystatus_jetsam_snapshot_count
3898 max_count = kern.globals.memorystatus_jetsam_snapshot_max
3899
3900 if (show_all_entries == True):
3901 count = max_count
3902 else:
3903 count = valid_count
3904
3905 print "{:s}".format(valid_count)
3906 print "{:s}".format(max_count)
3907
3908 if int(count) == 0:
3909 print "The jetsam snapshot is empty."
3910 print "Use -A to force dump all entries (regardless of valid count)"
3911 return
3912
3913 # Dumps the snapshot header info
3914 print lldb_run_command('p *memorystatus_jetsam_snapshot')
3915
cb323159 3916 hdr_format = "{0: >32s} {1: >5s} {2: >4s} {3: >6s} {4: >6s} {5: >20s} {6: >20s} {7: >20s} {8: >5s} {9: >10s} {10: >6s} {11: >6s} {12: >10s} {13: >15s} {14: >15s} {15: >15s}"
39037602 3917 if (show_footprint_details == True):
cb323159 3918 hdr_format += "{16: >15s} {17: >15s} {18: >12s} {19: >12s} {20: >17s} {21: >10s} {22: >13s} {23: >10s}"
39037602
A
3919
3920
3921 if (show_footprint_details == False):
cb323159
A
3922 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax')
3923 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)')
39037602 3924 else:
cb323159
A
3925 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
3926 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
39037602
A
3927
3928
3929 entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
3930 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
3931 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
cb323159 3932 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} "\
39037602
A
3933 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
3934
3935 if (show_footprint_details == True):
3936 entry_format += "{e.jse_internal_pages: >15d} "\
3937 "{e.jse_internal_compressed_pages: >15d} "\
3938 "{e.jse_iokit_mapped_pages: >12d} "\
3939 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
3940 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
3941 "{e.jse_alternate_accounting_pages: >10d} "\
3942 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
3943 "{e.jse_page_table_pages: >10d}"
3944
3945 snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
3946 idx = 0
3947 while idx < count:
cb323159 3948 current_entry = dereference(Cast(addressof(snapshot_list[idx]), 'jetsam_snapshot_entry *'))
39037602
A
3949 print entry_format.format(index=idx, e=current_entry)
3950 idx +=1
3951 return
3952
3953# EndMacro: showjetsamsnapshot
813fb2f6
A
3954
3955# Macro: showvnodecleanblk/showvnodedirtyblk
3956
3957def _GetBufSummary(buf):
3958 """ Get a summary of important information out of a buf_t.
3959 """
3960 initial = "(struct buf) {0: <#0x} ="
3961
3962 # List all of the fields in this buf summary.
3963 entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
3964 buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
3965 buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
3966 buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
3967 buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
3968 buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
3969
3970 # Join an (already decent) string representation of each field
3971 # with newlines and indent the region.
3972 joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ")
3973
3974 # Add the total string representation to our title and return it.
3975 out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n"
3976 return out_str
3977
3978def _ShowVnodeBlocks(dirty=True, cmd_args=None):
3979 """ Display info about all [dirty|clean] blocks in a vnode.
3980 """
3981 if cmd_args == None or len(cmd_args) < 1:
3982 print "Please provide a valid vnode argument."
3983 return
3984
3985 vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
3986 list_head = vnodeval.v_cleanblkhd;
3987 if dirty:
3988 list_head = vnodeval.v_dirtyblkhd
3989
3990 print "Blocklist for vnode {}:".format(cmd_args[0])
3991
3992 i = 0
3993 for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
3994 # For each block (buf_t) in the appropriate list,
3995 # ask for a summary and print it.
3996 print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
3997 i += 1
3998 return
3999
4000@lldb_command('showvnodecleanblk')
4001def ShowVnodeCleanBlocks(cmd_args=None):
4002 """ Display info about all clean blocks in a vnode.
4003 usage: showvnodecleanblk <address of vnode>
4004 """
4005 _ShowVnodeBlocks(False, cmd_args)
4006
4007@lldb_command('showvnodedirtyblk')
4008def ShowVnodeDirtyBlocks(cmd_args=None):
4009 """ Display info about all dirty blocks in a vnode.
4010 usage: showvnodedirtyblk <address of vnode>
4011 """
4012 _ShowVnodeBlocks(True, cmd_args)
4013
4014# EndMacro: showvnodecleanblk/showvnodedirtyblk
d9a64523
A
4015
4016
4017@lldb_command("vm_page_lookup_in_map")
4018def VmPageLookupInMap(cmd_args=None):
4019 """Lookup up a page at a virtual address in a VM map
4020 usage: vm_page_lookup_in_map <map> <vaddr>
4021 """
4022 if cmd_args == None or len(cmd_args) < 2:
4023 print "Invalid argument.", VmPageLookupInMap.__doc__
4024 return
4025 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
4026 vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
4027 print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map)
4028 vm_page_lookup_in_map(map, vaddr)
4029
4030def vm_page_lookup_in_map(map, vaddr):
4031 vaddr = unsigned(vaddr)
4032 vme_list_head = map.hdr.links
4033 vme_ptr_type = GetType('vm_map_entry *')
4034 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
4035 if unsigned(vme.links.start) > vaddr:
4036 break
4037 if unsigned(vme.links.end) <= vaddr:
4038 continue
4039 offset_in_vme = vaddr - unsigned(vme.links.start)
4040 print " offset {:#018x} in map entry {: <#018x} [{:#018x}:{:#018x}] object {: <#018x} offset {:#018x}".format(offset_in_vme, vme, unsigned(vme.links.start), unsigned(vme.links.end), vme.vme_object.vmo_object, unsigned(vme.vme_offset) & ~0xFFF)
4041 offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF)
4042 if vme.is_sub_map:
4043 print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap)
4044 vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object)
4045 else:
4046 vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object)
4047
4048@lldb_command("vm_page_lookup_in_object")
4049def VmPageLookupInObject(cmd_args=None):
4050 """Lookup up a page at a given offset in a VM object
4051 usage: vm_page_lookup_in_object <object> <offset>
4052 """
4053 if cmd_args == None or len(cmd_args) < 2:
4054 print "Invalid argument.", VmPageLookupInObject.__doc__
4055 return
4056 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
4057 offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
4058 print "offset {:#018x} in object {: <#018x}".format(offset, object)
4059 vm_page_lookup_in_object(object, offset)
4060
4061def vm_page_lookup_in_object(object, offset):
4062 offset = unsigned(offset)
4063 page_size = kern.globals.page_size
4064 trunc_offset = offset & ~(page_size - 1)
4065 print " offset {:#018x} in VM object {: <#018x}".format(offset, object)
4066 hash_id = _calc_vm_page_hash(object, trunc_offset)
4067 page_list = kern.globals.vm_page_buckets[hash_id].page_list
4068 page = _vm_page_unpack_ptr(page_list)
4069 while page != 0:
4070 m = kern.GetValueFromAddress(page, 'vm_page_t')
4071 m_object_val = _vm_page_unpack_ptr(m.vmp_object)
4072 m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
4073 if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
4074 page = _vm_page_unpack_ptr(m.vmp_next_m)
4075 continue
4076 print " resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))
4077 return
4078 if object.pager and object.pager_ready:
4079 offset_in_pager = trunc_offset + unsigned(object.paging_offset)
4080 if not object.internal:
4081 print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)
4082 return
4083 pager = Cast(object.pager, 'compressor_pager *')
4084 ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
4085 if ret:
4086 return
4087 if object.shadow and not object.phys_contiguous:
4088 offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
4089 vm_page_lookup_in_object(object.shadow, offset_in_shadow)
4090 return
4091 print " page is absent and will be zero-filled on demand"
4092 return
4093
4094@lldb_command("vm_page_lookup_in_compressor_pager")
4095def VmPageLookupInCompressorPager(cmd_args=None):
4096 """Lookup up a page at a given offset in a compressor pager
4097 usage: vm_page_lookup_in_compressor_pager <pager> <offset>
4098 """
4099 if cmd_args == None or len(cmd_args) < 2:
4100 print "Invalid argument.", VmPageLookupInCompressorPager.__doc__
4101 return
4102 pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
4103 offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
4104 print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)
4105 vm_page_lookup_in_compressor_pager(pager, offset)
4106
4107def vm_page_lookup_in_compressor_pager(pager, offset):
4108 offset = unsigned(offset)
4109 page_size = unsigned(kern.globals.page_size)
4110 page_num = unsigned(offset / page_size)
4111 if page_num > pager.cpgr_num_slots:
4112 print " *** ERROR: vm_page_lookup_in_compressor_pager({: <#018x},{:#018x}): page_num {:#x} > num_slots {:#x}".format(pager, offset, page_num, pager.cpgr_num_slots)
4113 return 0
4114 slots_per_chunk = 512 / sizeof ('compressor_slot_t')
4115 num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk)
4116 if num_chunks > 1:
4117 chunk_idx = unsigned(page_num / slots_per_chunk)
4118 chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
4119 slot_idx = unsigned(page_num % slots_per_chunk)
4120 slot = GetObjectAtIndexFromArray(chunk, slot_idx)
4121 slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
4122 elif pager.cpgr_num_slots > 2:
4123 slot_idx = page_num
4124 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
4125 slot_str = "dslots[{:d}]".format(slot_idx)
4126 else:
4127 slot_idx = page_num
4128 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
4129 slot_str = "eslots[{:d}]".format(slot_idx)
4130 print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)
4131 if slot == 0:
4132 return 0
4133 slot_value = dereference(slot)
4134 print " value {:#010x}".format(slot_value)
4135 vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
4136 return 1
4137
4138@lldb_command("vm_page_lookup_in_compressor")
4139def VmPageLookupInCompressor(cmd_args=None):
4140 """Lookup up a page in a given compressor slot
4141 usage: vm_page_lookup_in_compressor <slot>
4142 """
4143 if cmd_args == None or len(cmd_args) < 1:
4144 print "Invalid argument.", VmPageLookupInCompressor.__doc__
4145 return
4146 slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
4147 print "compressor slot {: <#018x}".format(slot)
4148 vm_page_lookup_in_compressor(slot)
4149
4150C_SV_CSEG_ID = ((1 << 22) - 1)
4151
4152def vm_page_lookup_in_compressor(slot_ptr):
4153 slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
4154 slot_value = dereference(slot_ptr)
4155 slot = Cast(slot_value, 'c_slot_mapping')
4156 print slot
4157 print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)
4158 if slot_ptr == 0:
4159 return
4160 if slot.s_cseg == C_SV_CSEG_ID:
4161 sv = kern.globals.c_segment_sv_hash_table
4162 print "single value[{:#d}]: ref {:d} value {:#010x}".format(slot.s_cindx, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_ref, sv[slot.s_cindx].c_sv_he_un.c_sv_he.c_sv_he_data)
4163 return
4164 if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
4165 print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))
4166 return
4167 c_segments = kern.globals.c_segments
4168 c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
4169 c_seg = c_segments_elt.c_seg
4170 c_no_data = 0
4171 if hasattr(c_seg, 'c_state'):
4172 c_state = c_seg.c_state
4173 if c_state == 0:
4174 c_state_str = "C_IS_EMPTY"
4175 c_no_data = 1
4176 elif c_state == 1:
4177 c_state_str = "C_IS_FREE"
4178 c_no_data = 1
4179 elif c_state == 2:
4180 c_state_str = "C_IS_FILLING"
4181 elif c_state == 3:
4182 c_state_str = "C_ON_AGE_Q"
4183 elif c_state == 4:
4184 c_state_str = "C_ON_SWAPOUT_Q"
4185 elif c_state == 5:
4186 c_state_str = "C_ON_SWAPPEDOUT_Q"
4187 c_no_data = 1
4188 elif c_state == 6:
4189 c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
4190 c_no_data = 1
4191 elif c_state == 7:
4192 c_state_str = "C_ON_SWAPPEDIN_Q"
4193 elif c_state == 8:
4194 c_state_str = "C_ON_MAJORCOMPACT_Q"
4195 elif c_state == 9:
4196 c_state_str = "C_ON_BAD_Q"
4197 c_no_data = 1
4198 else:
4199 c_state_str = "<unknown>"
4200 else:
4201 c_state = -1
4202 c_state_str = "<no c_state field>"
4203 print "c_segments[{:d}] {: <#018x} c_seg {: <#018x} c_state {:#x}={:s}".format(slot.s_cseg-1, c_segments_elt, c_seg, c_state, c_state_str)
4204 c_indx = unsigned(slot.s_cindx)
4205 if hasattr(c_seg, 'c_slot_var_array'):
4206 c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
4207 if c_indx < c_seg_fixed_array_len:
4208 cs = c_seg.c_slot_fixed_array[c_indx]
4209 else:
4210 cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
4211 else:
4212 C_SEG_SLOT_ARRAY_SIZE = 64
4213 C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
4214 cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
4215 print cs
4216 c_slot_unpacked_ptr = (unsigned(cs.c_packed_ptr) << 2) + vm_min_kernel_and_kext_address()
4217 print "c_slot {: <#018x} c_offset {:#x} c_size {:#x} c_packed_ptr {:#x} (unpacked: {: <#018x})".format(cs, cs.c_offset, cs.c_size, cs.c_packed_ptr, unsigned(c_slot_unpacked_ptr))
4218 if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
4219 print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)
4220 if c_no_data == 0:
4221 c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
4222 c_size = cs.c_size
4223 cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
4224 print cmd
4225 print lldb_run_command(cmd)
4226 else:
4227 print "<no compressed data>"
4228
4229def vm_min_kernel_and_kext_address(cmd_args=None):
4230 if hasattr(kern.globals, 'vm_min_kernel_and_kext_address'):
4231 return unsigned(kern.globals.vm_min_kernel_and_kext_address)
4232 elif kern.arch == 'x86_64':
4233 return unsigned(0xffffff7f80000000)
4234 elif kern.arch == 'arm64':
4235 return unsigned(0xffffff8000000000)
4236 elif kern.arch == 'arm':
4237 return unsigned(0x80000000)
4238 else:
4239 print "vm_min_kernel_and_kext_address(): unknown arch '{:s}'".format(kern.arch)
4240 return unsigned(0)
4241
4242def print_hex_data(data, begin_offset=0, desc=""):
4243 """ print on stdout "hexdump -C < data" like output
4244 params:
4245 data - bytearray or array of int where each int < 255
4246 begin_offset - int offset that should be printed in left column
4247 desc - str optional description to print on the first line to describe data
4248 """
4249 if desc:
4250 print "{}:".format(desc)
4251 index = 0
4252 total_len = len(data)
4253 hex_buf = ""
4254 char_buf = ""
4255 while index < total_len:
4256 hex_buf += " {:02x}".format(data[index])
4257 if data[index] < 0x20 or data[index] > 0x7e:
4258 char_buf += "."
4259 else:
4260 char_buf += "{:c}".format(data[index])
4261 index += 1
4262 if index and index % 8 == 0:
4263 hex_buf += " "
4264 if index > 1 and (index % 16) == 0:
4265 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
4266 hex_buf = ""
4267 char_buf = ""
4268 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
4269 return
4270
4271@lldb_command('vm_scan_all_pages')
4272def VMScanAllPages(cmd_args=None):
4273 """Scans the vm_pages[] array
4274 """
4275 vm_pages_count = kern.globals.vm_pages_count
4276 vm_pages = kern.globals.vm_pages
4277
4278 free_count = 0
4279 local_free_count = 0
4280 active_count = 0
4281 local_active_count = 0
4282 inactive_count = 0
4283 speculative_count = 0
4284 throttled_count = 0
4285 wired_count = 0
4286 compressor_count = 0
4287 pageable_internal_count = 0
4288 pageable_external_count = 0
4289 secluded_count = 0
4290 secluded_free_count = 0
4291 secluded_inuse_count = 0
4292
4293 i = 0
4294 while i < vm_pages_count:
4295
4296 if i % 10000 == 0:
4297 print "{:d}/{:d}...\n".format(i,vm_pages_count)
4298
4299 m = vm_pages[i]
4300
4301 internal = 0
4302 external = 0
4303 m_object_val = _vm_page_unpack_ptr(m.vmp_object)
4304
4305 if m_object:
4306 if m_object.internal:
4307 internal = 1
4308 else:
4309 external = 1
4310
4311 if m.vmp_wire_count != 0 and m.vmp_local == 0:
4312 wired_count = wired_count + 1
4313 pageable = 0
4314 elif m.vmp_throttled:
4315 throttled_count = throttled_count + 1
4316 pageable = 0
4317 elif m.vmp_active:
4318 active_count = active_count + 1
4319 pageable = 1
4320 elif m.vmp_local:
4321 local_active_count = local_active_count + 1
4322 pageable = 0
4323 elif m.vmp_inactive:
4324 inactive_count = inactive_count + 1
4325 pageable = 1
4326 elif m.vmp_speculative:
4327 speculative_count = speculative_count + 1
4328 pageable = 0
4329 elif m.vmp_free:
4330 free_count = free_count + 1
4331 pageable = 0
4332 elif m.vmp_secluded:
4333 secluded_count = secluded_count + 1
4334 if m_object == 0:
4335 secluded_free_count = secluded_free_count + 1
4336 else:
4337 secluded_inuse_count = secluded_inuse_count + 1
4338 pageable = 0
4339 elif m_object == 0 and m.vmp_busy:
4340 local_free_count = local_free_count + 1
4341 pageable = 0
4342 elif m.vmp_compressor:
4343 compressor_count = compressor_count + 1
4344 pageable = 0
4345 else:
4346 print "weird page vm_pages[{:d}]?\n".format(i)
4347 pageable = 0
4348
4349 if pageable:
4350 if internal:
4351 pageable_internal_count = pageable_internal_count + 1
4352 else:
4353 pageable_external_count = pageable_external_count + 1
4354 i = i + 1
4355
4356 print "vm_pages_count = {:d}\n".format(vm_pages_count)
4357
4358 print "wired_count = {:d}\n".format(wired_count)
4359 print "throttled_count = {:d}\n".format(throttled_count)
4360 print "active_count = {:d}\n".format(active_count)
4361 print "local_active_count = {:d}\n".format(local_active_count)
4362 print "inactive_count = {:d}\n".format(inactive_count)
4363 print "speculative_count = {:d}\n".format(speculative_count)
4364 print "free_count = {:d}\n".format(free_count)
4365 print "local_free_count = {:d}\n".format(local_free_count)
4366 print "compressor_count = {:d}\n".format(compressor_count)
4367
4368 print "pageable_internal_count = {:d}\n".format(pageable_internal_count)
4369 print "pageable_external_count = {:d}\n".format(pageable_external_count)
4370 print "secluded_count = {:d}\n".format(secluded_count)
4371 print "secluded_free_count = {:d}\n".format(secluded_free_count)
4372 print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count)
4373
4374
4375@lldb_command('show_all_vm_named_entries')
4376def ShowAllVMNamedEntries(cmd_args=None):
4377 """ Routine to print a summary listing of all the VM named entries
4378 """
4379 queue_len = kern.globals.vm_named_entry_count
4380 queue_head = kern.globals.vm_named_entry_list
4381
4382 print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len)
4383
4384 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>3s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tag","owner","pid","process")
4385 idx = 0
4386 for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'):
4387 idx += 1
4388 showmemoryentry(entry, idx, queue_len)
4389
4390@lldb_command('show_vm_named_entry')
4391def ShowVMNamedEntry(cmd_args=None):
4392 """ Routine to print a VM named entry
4393 """
4394 if cmd_args == None or len(cmd_args) < 1:
4395 print "Invalid argument.", ShowMapVMNamedEntry.__doc__
4396 return
4397 named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
4398 showmemoryentry(named_entry, 0, 0)
4399
4400def showmemoryentry(entry, idx=0, queue_len=0):
4401 """ Routine to print out a summary a VM memory entry
4402 params:
4403 entry - core.value : a object of type 'struct vm_named_entry *'
4404 returns:
4405 None
4406 """
4407 show_pager_info = True
4408 show_all_shadows = True
4409
4410 backing = ""
4411 if entry.is_sub_map == 1:
4412 backing += "SUBMAP"
4413 if entry.is_copy == 1:
4414 backing += "COPY"
4415 if entry.is_sub_map == 0 and entry.is_copy == 0:
4416 backing += "OBJECT"
4417 prot=""
4418 if entry.protection & 0x1:
4419 prot += "r"
4420 else:
4421 prot += "-"
4422 if entry.protection & 0x2:
4423 prot += "w"
4424 else:
4425 prot += "-"
4426 if entry.protection & 0x4:
4427 prot += "x"
4428 else:
4429 prot += "-"
4430 extra_str = ""
4431 if hasattr(entry, 'named_entry_alias'):
4432 extra_str += " alias={:d}".format(entry.named_entry_alias)
4433 if hasattr(entry, 'named_entry_port'):
4434 extra_str += " port={:#016x}".format(entry.named_entry_port)
4435 print "{:>6d}/{:<6d} {: <#018x} ref={:d} prot={:d}/{:s} type={:s} backing={: <#018x} offset={:#016x} dataoffset={:#016x} size={:#016x}{:s}\n".format(idx,queue_len,entry,entry.ref_count,entry.protection,prot,backing,entry.backing.object,entry.offset,entry.data_offset,entry.size,extra_str)
4436 if entry.is_sub_map == 1:
4437 showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
4438 if entry.is_copy == 1:
94ff46dc 4439 showmapcopyvme(entry.backing.copy, 0, 0, show_pager_info, show_all_shadows, 0)
d9a64523
A
4440 if entry.is_sub_map == 0 and entry.is_copy == 0:
4441 showvmobject(entry.backing.object, entry.offset, entry.size, show_pager_info, show_all_shadows)
4442
4443
4444def IterateRBTreeEntry2(element, element_type, field_name1, field_name2):
4445 """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
4446 element - value : Value object for rbh_root
4447 element_type - str : Type of the link element
4448 field_name - str : Name of the field in link element's structure
4449 returns:
4450 A generator does not return. It is used for iterating
4451 value : an object thats of type (element_type) head->sle_next. Always a pointer object
4452 """
4453 elt = element.__getattr__('rbh_root')
4454 if type(element_type) == str:
4455 element_type = gettype(element_type)
4456 charp_type = gettype('char *');
4457
4458 # Walk to find min
4459 parent = elt
4460 while unsigned(elt) != 0:
4461 parent = elt
4462 elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4463 elt = parent
4464
4465 # Now elt is min
4466 while unsigned(elt) != 0:
4467 yield elt
4468 # implementation cribbed from RB_NEXT in libkern/tree.h
4469 right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type)
4470 if unsigned(right) != 0:
4471 elt = right
4472 left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4473 while unsigned(left) != 0:
4474 elt = left
4475 left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type)
4476 else:
4477
4478 # avoid using GetValueFromAddress
4479 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4480 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4481 parent = cast(parent, element_type)
4482
4483 if unsigned(parent) != 0:
4484 left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4485 if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
4486 elt = parent
4487 else:
4488 if unsigned(parent) != 0:
4489 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
4490 while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
4491 elt = parent
4492
4493 # avoid using GetValueFromAddress
4494 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4495 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4496 parent = cast(parent, element_type)
4497
4498 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
4499
4500 # avoid using GetValueFromAddress
4501 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4502 elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4503 elt = cast(elt, element_type)
4504
4505
4506@lldb_command("showmaprb")
4507def ShowMapRB(cmd_args=None):
4508 """Routine to print out a VM map's RB tree
4509 usage: showmaprb <vm_map>
4510 """
4511 if cmd_args == None or len(cmd_args) < 1:
4512 print "Invalid argument.", ShowMapRB.__doc__
4513 return
4514 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
4515 print GetVMMapSummary.header
4516 print GetVMMapSummary(map_val)
4517 vme_rb_root = map_val.hdr.rb_head_store
4518 vme_ptr_type = GetType('struct vm_map_entry *')
4519 print GetVMEntrySummary.header
4520 for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'):
4521 print GetVMEntrySummary(vme)
4522 return None
4523
4524@lldb_command('show_all_owned_objects', 'T')
4525def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
4526 """ Routine to print the list of VM objects owned by each task
4527 -T: show only ledger-tagged objects
4528 """
4529 showonlytagged = False
4530 if "-T" in cmd_options:
4531 showonlytagged = True
4532 for task in kern.tasks:
4533 ShowTaskOwnedVmObjects(task, showonlytagged)
4534
4535@lldb_command('show_task_owned_objects', 'T')
4536def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
4537 """ Routine to print the list of VM objects owned by the specified task
4538 -T: show only ledger-tagged objects
4539 """
4540 showonlytagged = False
4541 if "-T" in cmd_options:
4542 showonlytagged = True
4543 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
4544 ShowTaskOwnedVmObjects(task, showonlytagged)
4545
4546def ShowTaskOwnedVmObjects(task, showonlytagged=False):
4547 """ Routine to print out a summary listing of all the entries in a vm_map
4548 params:
4549 task - core.value : a object of type 'task *'
4550 returns:
4551 None
4552 """
4553 taskobjq_total = lambda:None
4554 taskobjq_total.objects = 0
4555 taskobjq_total.vsize = 0
4556 taskobjq_total.rsize = 0
4557 taskobjq_total.wsize = 0
4558 taskobjq_total.csize = 0
4559 vmo_list_head = task.task_objq
4560 vmo_ptr_type = GetType('vm_object *')
4561 idx = 0
4562 for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
4563 idx += 1
4564 if not showonlytagged or vmo.vo_ledger_tag != 0:
4565 if taskobjq_total.objects == 0:
4566 print ' \n'
4567 print GetTaskSummary.header + ' ' + GetProcSummary.header
4568 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
4569 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:>2s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","tg","owner","pid","process")
4570 ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
4571 if taskobjq_total.objects != 0:
4572 print " total:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(taskobjq_total.objects, taskobjq_total.vsize, taskobjq_total.rsize, taskobjq_total.wsize, taskobjq_total.csize)
4573 return None
4574
4575def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
4576 """ Routine to print out a VM object owned by a task
4577 params:
4578 object - core.value : a object of type 'struct vm_object *'
4579 returns:
4580 None
4581 """
4582 page_size = kern.globals.page_size
4583 if object.purgable == 0:
4584 purgable = "N"
4585 elif object.purgable == 1:
4586 purgable = "V"
4587 elif object.purgable == 2:
4588 purgable = "E"
4589 elif object.purgable == 3:
4590 purgable = "D"
4591 else:
4592 purgable = "?"
4593 if object.pager == 0:
4594 compressed_count = 0
4595 else:
4596 compressor_pager = Cast(object.pager, 'compressor_pager *')
4597 compressed_count = compressor_pager.cpgr_num_slots_occupied
4598
4599 print "{:>6d}/{:<6d} {: <#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:>2d} {: <#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_ledger_tag, object.vo_un2.vou_owner,GetProcPIDForObjectOwner(object.vo_un2.vou_owner),GetProcNameForObjectOwner(object.vo_un2.vou_owner))
4600
4601 taskobjq_total.objects += 1
4602 taskobjq_total.vsize += object.vo_un1.vou_size/page_size
4603 taskobjq_total.rsize += object.resident_page_count
4604 taskobjq_total.wsize += object.wired_page_count
4605 taskobjq_total.csize += compressed_count
4606
4607def GetProcPIDForObjectOwner(owner):
4608 """ same as GetProcPIDForTask() but deals with -1 for a disowned object
4609 """
4610 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
4611 return -1
4612 return GetProcPIDForTask(owner)
4613
4614def GetProcNameForObjectOwner(owner):
4615 """ same as GetProcNameForTask() but deals with -1 for a disowned object
4616 """
4617 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
4618 return "<disowned>"
4619 return GetProcNameForTask(owner)
4620
4621def GetDescForNamedEntry(mem_entry):
4622 out_str = "\n"
4623 out_str += "\t\tmem_entry {:#08x} ref:{:d} offset:{:#08x} size:{:#08x} prot{:d} backing {:#08x}".format(mem_entry, mem_entry.ref_count, mem_entry.offset, mem_entry.size, mem_entry.protection, mem_entry.backing.object)
4624 if mem_entry.is_sub_map:
4625 out_str += " is_sub_map"
4626 elif mem_entry.is_copy:
4627 out_str += " is_copy"
4628 else:
4629 out_str += " is_object"
4630 return out_str