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