]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/memory.py
xnu-4903.241.1.tar.gz
[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 return (unsigned(addressof(kern.globals.vm_pages[masked_page])))
3297 return ((unsigned(page) << unsigned(ptr_shift)) + unsigned(min_addr))
3298
3299 @lldb_command('calcvmpagehash')
3300 def CalcVMPageHash(cmd_args=None):
3301 """ Get the page bucket corresponding to the provided object and offset.
3302 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3303 """
3304 if cmd_args == None or len(cmd_args) < 2:
3305 raise ArgumentError("Please specify an object and offset.")
3306
3307 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3308 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3309
3310 hash_id = _calc_vm_page_hash(obj, off)
3311
3312 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
3313 return None
3314
3315 def _calc_vm_page_hash(obj, off):
3316 bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
3317 hash_mask = (int) (kern.globals.vm_page_hash_mask)
3318
3319 one = (obj * bucket_hash) & 0xFFFFFFFF
3320 two = off >> unsigned(kern.globals.page_shift)
3321 three = two ^ bucket_hash
3322 four = one + three
3323 hash_id = four & hash_mask
3324
3325 return hash_id
3326
3327 VM_PAGE_IS_WIRED = 1
3328
3329 @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"))
3330 @lldb_command('vmobjectwalkpages', 'SBNQP:')
3331 def VMObjectWalkPages(cmd_args=None, cmd_options={}):
3332 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
3333 specifically look for this page, highlighting it in the output or noting if it was not found. For
3334 each page, we confirm that it points to the object. We also keep track of the number of pages we
3335 see and compare this to the object's resident page count field.
3336 Usage:
3337 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
3338 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
3339 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
3340 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)
3341 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
3342 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
3343
3344 """
3345
3346 if (cmd_args == None or len(cmd_args) < 1):
3347 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
3348
3349 out_string = ""
3350
3351 obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3352
3353 page = 0
3354 if "-P" in cmd_options:
3355 page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
3356
3357 stop = 0
3358 if "-S" in cmd_options:
3359 if page == 0:
3360 raise ArgumentError("-S can only be passed when a page is specified with -P")
3361 stop = 1
3362
3363 walk_backwards = False
3364 if "-B" in cmd_options:
3365 walk_backwards = True
3366
3367 quiet_mode = False
3368 if "-Q" in cmd_options:
3369 quiet_mode = True
3370
3371 if not quiet_mode:
3372 print VMObjectWalkPages.header
3373 format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
3374 first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
3375 second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
3376 second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
3377 second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
3378 second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
3379
3380 limit = 4096 #arbitrary limit of number of pages to walk
3381 ignore_limit = 0
3382 if "-N" in cmd_options:
3383 ignore_limit = 1
3384
3385 page_count = 0
3386 res_page_count = unsigned(obj.resident_page_count)
3387 page_found = False
3388 pages_seen = set()
3389
3390 for vmp in IterateQueue(obj.memq, "vm_page_t", "vmp_listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
3391 page_count += 1
3392 out_string = ""
3393 if (page != 0 and not(page_found) and vmp == page):
3394 out_string += "******"
3395 page_found = True
3396
3397 if page != 0 or quiet_mode:
3398 if (page_count % 1000) == 0:
3399 print "traversed %d pages ...\n" % (page_count)
3400 else:
3401 out_string += format_string.format(page_count, res_page_count, vmp, vmp.vmp_offset, _vm_page_unpack_ptr(vmp.listq.next), _vm_page_get_phys_page(vmp), vmp.vmp_wire_count)
3402 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,
3403 vmp.vmp_private, vmp.vmp_reference)
3404
3405 if hasattr(vmp,'slid'):
3406 vmp_slid = vmp.slid
3407 else:
3408 vmp_slid = 0
3409 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,
3410 vmp.vmp_pmapped, vmp.vmp_xpmapped, vmp.vmp_wpmapped, vmp.vmp_free_when_done, vmp.vmp_absent,
3411 vmp.vmp_error, vmp.vmp_dirty, vmp.vmp_cleaning, vmp.vmp_precious, vmp.vmp_overwriting,
3412 vmp.vmp_restart, vmp.vmp_unusual, 0, 0,
3413 vmp.vmp_cs_validated, vmp.vmp_cs_tainted, vmp.vmp_cs_nx, vmp.vmp_reusable, vmp.vmp_lopage, vmp_slid,
3414 vmp.vmp_written_by_kernel)
3415
3416 if (vmp in pages_seen):
3417 print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
3418 return
3419
3420 if (_vm_page_unpack_ptr(vmp.vmp_object) != unsigned(obj)):
3421 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)))
3422 return
3423
3424 if (vmp.vmp_q_state == VM_PAGE_IS_WIRED) and (vmp.vmp_wire_count == 0):
3425 print out_string + " page in wired state with wire_count of 0\n"
3426 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
3427 print "stopping...\n"
3428 return
3429
3430 if ((vmp.vmp_unused_page_bits != 0) or (vmp.vmp_unused_object_bits != 0)):
3431 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,
3432 vmp.vmp_unused_object_bits)
3433 print "stopping...\n"
3434 return
3435
3436 pages_seen.add(vmp)
3437
3438 if False:
3439 hash_id = _calc_vm_page_hash(obj, vmp.vmp_offset)
3440 hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
3441 hash_page = _vm_page_unpack_ptr(hash_page_list)
3442 hash_page_t = 0
3443
3444 while (hash_page != 0):
3445 hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
3446 if hash_page_t == vmp:
3447 break
3448 hash_page = _vm_page_unpack_ptr(hash_page_t.vmp_next_m)
3449
3450 if (unsigned(vmp) != unsigned(hash_page_t)):
3451 print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
3452 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.vmp_offset)))
3453 return
3454
3455 if (page_count >= limit and not(ignore_limit)):
3456 print out_string + "Limit reached (%d pages), stopping..." % (limit)
3457 return
3458
3459 print out_string
3460
3461 if page_found and stop:
3462 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)))
3463 return
3464
3465 if (page != 0):
3466 print("page found? : %s\n" % page_found)
3467
3468 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)))
3469
3470
3471 @lldb_command("show_all_apple_protect_pagers")
3472 def ShowAllAppleProtectPagers(cmd_args=None):
3473 """Routine to print all apple_protect pagers
3474 usage: show_all_apple_protect_pagers
3475 """
3476 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")
3477 qhead = kern.globals.apple_protect_pager_queue
3478 qtype = GetType('apple_protect_pager *')
3479 qcnt = kern.globals.apple_protect_pager_count
3480 idx = 0
3481 for pager in IterateQueue(qhead, qtype, "pager_queue"):
3482 idx = idx + 1
3483 show_apple_protect_pager(pager, qcnt, idx)
3484
3485 @lldb_command("show_apple_protect_pager")
3486 def ShowAppleProtectPager(cmd_args=None):
3487 """Routine to print out info about an apple_protect pager
3488 usage: show_apple_protect_pager <pager>
3489 """
3490 if cmd_args == None or len(cmd_args) < 1:
3491 print "Invalid argument.", ShowAppleProtectPager.__doc__
3492 return
3493 pager = kern.GetValueFromAddress(cmd_args[0], 'apple_protect_pager_t')
3494 show_apple_protect_pager(pager, 1, 1)
3495
3496 def show_apple_protect_pager(pager, qcnt, idx):
3497 object = pager.backing_object
3498 shadow = object.shadow
3499 while shadow != 0:
3500 object = shadow
3501 shadow = object.shadow
3502 vnode_pager = Cast(object.pager,'vnode_pager *')
3503 filename = GetVnodePath(vnode_pager.vnode_handle)
3504 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)
3505
3506 @lldb_command("show_console_ring")
3507 def ShowConsoleRingData(cmd_args=None):
3508 """ Print console ring buffer stats and data
3509 """
3510 cr = kern.globals.console_ring
3511 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)
3512 pending_data = []
3513 for i in range(unsigned(cr.used)):
3514 idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
3515 pending_data.append("{:c}".format(cr.buffer[idx]))
3516
3517 if pending_data:
3518 print "Data:"
3519 print "".join(pending_data)
3520
3521 # Macro: showjetsamsnapshot
3522
3523 @lldb_command("showjetsamsnapshot", "DA")
3524 def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
3525 """ Dump entries in the jetsam snapshot table
3526 usage: showjetsamsnapshot [-D] [-A]
3527 Use -D flag to print extra physfootprint details
3528 Use -A flag to print all entries (regardless of valid count)
3529 """
3530
3531 # Not shown are uuid, user_data, cpu_time
3532
3533 global kern
3534
3535 show_footprint_details = False
3536 show_all_entries = False
3537
3538 if "-D" in cmd_options:
3539 show_footprint_details = True
3540
3541 if "-A" in cmd_options:
3542 show_all_entries = True
3543
3544 valid_count = kern.globals.memorystatus_jetsam_snapshot_count
3545 max_count = kern.globals.memorystatus_jetsam_snapshot_max
3546
3547 if (show_all_entries == True):
3548 count = max_count
3549 else:
3550 count = valid_count
3551
3552 print "{:s}".format(valid_count)
3553 print "{:s}".format(max_count)
3554
3555 if int(count) == 0:
3556 print "The jetsam snapshot is empty."
3557 print "Use -A to force dump all entries (regardless of valid count)"
3558 return
3559
3560 # Dumps the snapshot header info
3561 print lldb_run_command('p *memorystatus_jetsam_snapshot')
3562
3563 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}"
3564 if (show_footprint_details == True):
3565 hdr_format += "{17: >15s} {18: >15s} {19: >12s} {20: >12s} {21: >17s} {22: >10s} {23: >13s} {24: >10s}"
3566
3567
3568 if (show_footprint_details == False):
3569 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'max', 'purgeable', 'lifetimeMax')
3570 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)')
3571 else:
3572 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')
3573 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
3574
3575
3576 entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
3577 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
3578 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
3579 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} {e.max_pages: >15d} "\
3580 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
3581
3582 if (show_footprint_details == True):
3583 entry_format += "{e.jse_internal_pages: >15d} "\
3584 "{e.jse_internal_compressed_pages: >15d} "\
3585 "{e.jse_iokit_mapped_pages: >12d} "\
3586 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
3587 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
3588 "{e.jse_alternate_accounting_pages: >10d} "\
3589 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
3590 "{e.jse_page_table_pages: >10d}"
3591
3592 snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
3593 idx = 0
3594 while idx < count:
3595 current_entry = Cast(snapshot_list[idx], 'jetsam_snapshot_entry')
3596 print entry_format.format(index=idx, e=current_entry)
3597 idx +=1
3598 return
3599
3600 # EndMacro: showjetsamsnapshot
3601
3602 # Macro: showvnodecleanblk/showvnodedirtyblk
3603
3604 def _GetBufSummary(buf):
3605 """ Get a summary of important information out of a buf_t.
3606 """
3607 initial = "(struct buf) {0: <#0x} ="
3608
3609 # List all of the fields in this buf summary.
3610 entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
3611 buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
3612 buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
3613 buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
3614 buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
3615 buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
3616
3617 # Join an (already decent) string representation of each field
3618 # with newlines and indent the region.
3619 joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ")
3620
3621 # Add the total string representation to our title and return it.
3622 out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n"
3623 return out_str
3624
3625 def _ShowVnodeBlocks(dirty=True, cmd_args=None):
3626 """ Display info about all [dirty|clean] blocks in a vnode.
3627 """
3628 if cmd_args == None or len(cmd_args) < 1:
3629 print "Please provide a valid vnode argument."
3630 return
3631
3632 vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
3633 list_head = vnodeval.v_cleanblkhd;
3634 if dirty:
3635 list_head = vnodeval.v_dirtyblkhd
3636
3637 print "Blocklist for vnode {}:".format(cmd_args[0])
3638
3639 i = 0
3640 for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
3641 # For each block (buf_t) in the appropriate list,
3642 # ask for a summary and print it.
3643 print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
3644 i += 1
3645 return
3646
3647 @lldb_command('showvnodecleanblk')
3648 def ShowVnodeCleanBlocks(cmd_args=None):
3649 """ Display info about all clean blocks in a vnode.
3650 usage: showvnodecleanblk <address of vnode>
3651 """
3652 _ShowVnodeBlocks(False, cmd_args)
3653
3654 @lldb_command('showvnodedirtyblk')
3655 def ShowVnodeDirtyBlocks(cmd_args=None):
3656 """ Display info about all dirty blocks in a vnode.
3657 usage: showvnodedirtyblk <address of vnode>
3658 """
3659 _ShowVnodeBlocks(True, cmd_args)
3660
3661 # EndMacro: showvnodecleanblk/showvnodedirtyblk
3662
3663
3664 @lldb_command("vm_page_lookup_in_map")
3665 def VmPageLookupInMap(cmd_args=None):
3666 """Lookup up a page at a virtual address in a VM map
3667 usage: vm_page_lookup_in_map <map> <vaddr>
3668 """
3669 if cmd_args == None or len(cmd_args) < 2:
3670 print "Invalid argument.", VmPageLookupInMap.__doc__
3671 return
3672 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
3673 vaddr = kern.GetValueFromAddress(cmd_args[1], 'vm_map_offset_t')
3674 print "vaddr {:#018x} in map {: <#018x}".format(vaddr, map)
3675 vm_page_lookup_in_map(map, vaddr)
3676
3677 def vm_page_lookup_in_map(map, vaddr):
3678 vaddr = unsigned(vaddr)
3679 vme_list_head = map.hdr.links
3680 vme_ptr_type = GetType('vm_map_entry *')
3681 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
3682 if unsigned(vme.links.start) > vaddr:
3683 break
3684 if unsigned(vme.links.end) <= vaddr:
3685 continue
3686 offset_in_vme = vaddr - unsigned(vme.links.start)
3687 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)
3688 offset_in_object = offset_in_vme + (unsigned(vme.vme_offset) & ~0xFFF)
3689 if vme.is_sub_map:
3690 print "vaddr {:#018x} in map {: <#018x}".format(offset_in_object, vme.vme_object.vmo_submap)
3691 vm_page_lookup_in_map(vme.vme_object.vmo_submap, offset_in_object)
3692 else:
3693 vm_page_lookup_in_object(vme.vme_object.vmo_object, offset_in_object)
3694
3695 @lldb_command("vm_page_lookup_in_object")
3696 def VmPageLookupInObject(cmd_args=None):
3697 """Lookup up a page at a given offset in a VM object
3698 usage: vm_page_lookup_in_object <object> <offset>
3699 """
3700 if cmd_args == None or len(cmd_args) < 2:
3701 print "Invalid argument.", VmPageLookupInObject.__doc__
3702 return
3703 object = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3704 offset = kern.GetValueFromAddress(cmd_args[1], 'vm_object_offset_t')
3705 print "offset {:#018x} in object {: <#018x}".format(offset, object)
3706 vm_page_lookup_in_object(object, offset)
3707
3708 def vm_page_lookup_in_object(object, offset):
3709 offset = unsigned(offset)
3710 page_size = kern.globals.page_size
3711 trunc_offset = offset & ~(page_size - 1)
3712 print " offset {:#018x} in VM object {: <#018x}".format(offset, object)
3713 hash_id = _calc_vm_page_hash(object, trunc_offset)
3714 page_list = kern.globals.vm_page_buckets[hash_id].page_list
3715 page = _vm_page_unpack_ptr(page_list)
3716 while page != 0:
3717 m = kern.GetValueFromAddress(page, 'vm_page_t')
3718 m_object_val = _vm_page_unpack_ptr(m.vmp_object)
3719 m_object = kern.GetValueFromAddress(m_object_val, 'vm_object_t')
3720 if unsigned(m_object) != unsigned(object) or unsigned(m.vmp_offset) != unsigned(trunc_offset):
3721 page = _vm_page_unpack_ptr(m.vmp_next_m)
3722 continue
3723 print " resident page {: <#018x} phys {:#010x}".format(m, _vm_page_get_phys_page(m))
3724 return
3725 if object.pager and object.pager_ready:
3726 offset_in_pager = trunc_offset + unsigned(object.paging_offset)
3727 if not object.internal:
3728 print " offset {:#018x} in external '{:s}' {: <#018x}".format(offset_in_pager, object.pager.mo_pager_ops.memory_object_pager_name, object.pager)
3729 return
3730 pager = Cast(object.pager, 'compressor_pager *')
3731 ret = vm_page_lookup_in_compressor_pager(pager, offset_in_pager)
3732 if ret:
3733 return
3734 if object.shadow and not object.phys_contiguous:
3735 offset_in_shadow = offset + unsigned(object.vo_un2.vou_shadow_offset)
3736 vm_page_lookup_in_object(object.shadow, offset_in_shadow)
3737 return
3738 print " page is absent and will be zero-filled on demand"
3739 return
3740
3741 @lldb_command("vm_page_lookup_in_compressor_pager")
3742 def VmPageLookupInCompressorPager(cmd_args=None):
3743 """Lookup up a page at a given offset in a compressor pager
3744 usage: vm_page_lookup_in_compressor_pager <pager> <offset>
3745 """
3746 if cmd_args == None or len(cmd_args) < 2:
3747 print "Invalid argument.", VmPageLookupInCompressorPager.__doc__
3748 return
3749 pager = kern.GetValueFromAddress(cmd_args[0], 'compressor_pager_t')
3750 offset = kern.GetValueFromAddress(cmd_args[1], 'memory_object_offset_t')
3751 print "offset {:#018x} in compressor pager {: <#018x}".format(offset, pager)
3752 vm_page_lookup_in_compressor_pager(pager, offset)
3753
3754 def vm_page_lookup_in_compressor_pager(pager, offset):
3755 offset = unsigned(offset)
3756 page_size = unsigned(kern.globals.page_size)
3757 page_num = unsigned(offset / page_size)
3758 if page_num > pager.cpgr_num_slots:
3759 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)
3760 return 0
3761 slots_per_chunk = 512 / sizeof ('compressor_slot_t')
3762 num_chunks = unsigned((pager.cpgr_num_slots+slots_per_chunk-1) / slots_per_chunk)
3763 if num_chunks > 1:
3764 chunk_idx = unsigned(page_num / slots_per_chunk)
3765 chunk = pager.cpgr_slots.cpgr_islots[chunk_idx]
3766 slot_idx = unsigned(page_num % slots_per_chunk)
3767 slot = GetObjectAtIndexFromArray(chunk, slot_idx)
3768 slot_str = "islots[{:d}][{:d}]".format(chunk_idx, slot_idx)
3769 elif pager.cpgr_num_slots > 2:
3770 slot_idx = page_num
3771 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_dslots, slot_idx)
3772 slot_str = "dslots[{:d}]".format(slot_idx)
3773 else:
3774 slot_idx = page_num
3775 slot = GetObjectAtIndexFromArray(pager.cpgr_slots.cpgr_eslots, slot_idx)
3776 slot_str = "eslots[{:d}]".format(slot_idx)
3777 print " offset {:#018x} in compressor pager {: <#018x} {:s} slot {: <#018x}".format(offset, pager, slot_str, slot)
3778 if slot == 0:
3779 return 0
3780 slot_value = dereference(slot)
3781 print " value {:#010x}".format(slot_value)
3782 vm_page_lookup_in_compressor(Cast(slot, 'c_slot_mapping_t'))
3783 return 1
3784
3785 @lldb_command("vm_page_lookup_in_compressor")
3786 def VmPageLookupInCompressor(cmd_args=None):
3787 """Lookup up a page in a given compressor slot
3788 usage: vm_page_lookup_in_compressor <slot>
3789 """
3790 if cmd_args == None or len(cmd_args) < 1:
3791 print "Invalid argument.", VmPageLookupInCompressor.__doc__
3792 return
3793 slot = kern.GetValueFromAddress(cmd_args[0], 'compressor_slot_t *')
3794 print "compressor slot {: <#018x}".format(slot)
3795 vm_page_lookup_in_compressor(slot)
3796
3797 C_SV_CSEG_ID = ((1 << 22) - 1)
3798
3799 def vm_page_lookup_in_compressor(slot_ptr):
3800 slot_ptr = Cast(slot_ptr, 'compressor_slot_t *')
3801 slot_value = dereference(slot_ptr)
3802 slot = Cast(slot_value, 'c_slot_mapping')
3803 print slot
3804 print "compressor slot {: <#018x} -> {:#010x} cseg {:d} cindx {:d}".format(unsigned(slot_ptr), unsigned(slot_value), slot.s_cseg, slot.s_cindx)
3805 if slot_ptr == 0:
3806 return
3807 if slot.s_cseg == C_SV_CSEG_ID:
3808 sv = kern.globals.c_segment_sv_hash_table
3809 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)
3810 return
3811 if slot.s_cseg == 0 or unsigned(slot.s_cseg) > unsigned(kern.globals.c_segments_available):
3812 print "*** ERROR: s_cseg {:d} is out of bounds (1 - {:d})".format(slot.s_cseg, unsigned(kern.globals.c_segments_available))
3813 return
3814 c_segments = kern.globals.c_segments
3815 c_segments_elt = GetObjectAtIndexFromArray(c_segments, slot.s_cseg-1)
3816 c_seg = c_segments_elt.c_seg
3817 c_no_data = 0
3818 if hasattr(c_seg, 'c_state'):
3819 c_state = c_seg.c_state
3820 if c_state == 0:
3821 c_state_str = "C_IS_EMPTY"
3822 c_no_data = 1
3823 elif c_state == 1:
3824 c_state_str = "C_IS_FREE"
3825 c_no_data = 1
3826 elif c_state == 2:
3827 c_state_str = "C_IS_FILLING"
3828 elif c_state == 3:
3829 c_state_str = "C_ON_AGE_Q"
3830 elif c_state == 4:
3831 c_state_str = "C_ON_SWAPOUT_Q"
3832 elif c_state == 5:
3833 c_state_str = "C_ON_SWAPPEDOUT_Q"
3834 c_no_data = 1
3835 elif c_state == 6:
3836 c_state_str = "C_ON_SWAPPEDOUTSPARSE_Q"
3837 c_no_data = 1
3838 elif c_state == 7:
3839 c_state_str = "C_ON_SWAPPEDIN_Q"
3840 elif c_state == 8:
3841 c_state_str = "C_ON_MAJORCOMPACT_Q"
3842 elif c_state == 9:
3843 c_state_str = "C_ON_BAD_Q"
3844 c_no_data = 1
3845 else:
3846 c_state_str = "<unknown>"
3847 else:
3848 c_state = -1
3849 c_state_str = "<no c_state field>"
3850 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)
3851 c_indx = unsigned(slot.s_cindx)
3852 if hasattr(c_seg, 'c_slot_var_array'):
3853 c_seg_fixed_array_len = kern.globals.c_seg_fixed_array_len
3854 if c_indx < c_seg_fixed_array_len:
3855 cs = c_seg.c_slot_fixed_array[c_indx]
3856 else:
3857 cs = GetObjectAtIndexFromArray(c_seg.c_slot_var_array, c_indx - c_seg_fixed_array_len)
3858 else:
3859 C_SEG_SLOT_ARRAY_SIZE = 64
3860 C_SEG_SLOT_ARRAY_MASK = C_SEG_SLOT_ARRAY_SIZE - 1
3861 cs = GetObjectAtIndexFromArray(c_seg.c_slots[c_indx / C_SEG_SLOT_ARRAY_SIZE], c_indx & C_SEG_SLOT_ARRAY_MASK)
3862 print cs
3863 c_slot_unpacked_ptr = (unsigned(cs.c_packed_ptr) << 2) + vm_min_kernel_and_kext_address()
3864 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))
3865 if unsigned(slot_ptr) != unsigned(c_slot_unpacked_ptr):
3866 print "*** ERROR: compressor slot {: <#018x} points back to {: <#018x} instead of itself".format(slot_ptr, c_slot_unpacked_ptr)
3867 if c_no_data == 0:
3868 c_data = c_seg.c_store.c_buffer + (4 * cs.c_offset)
3869 c_size = cs.c_size
3870 cmd = "memory read {: <#018x} {: <#018x} --force".format(c_data, c_data + c_size)
3871 print cmd
3872 print lldb_run_command(cmd)
3873 else:
3874 print "<no compressed data>"
3875
3876 def vm_min_kernel_and_kext_address(cmd_args=None):
3877 if hasattr(kern.globals, 'vm_min_kernel_and_kext_address'):
3878 return unsigned(kern.globals.vm_min_kernel_and_kext_address)
3879 elif kern.arch == 'x86_64':
3880 return unsigned(0xffffff7f80000000)
3881 elif kern.arch == 'arm64':
3882 return unsigned(0xffffff8000000000)
3883 elif kern.arch == 'arm':
3884 return unsigned(0x80000000)
3885 else:
3886 print "vm_min_kernel_and_kext_address(): unknown arch '{:s}'".format(kern.arch)
3887 return unsigned(0)
3888
3889 def print_hex_data(data, begin_offset=0, desc=""):
3890 """ print on stdout "hexdump -C < data" like output
3891 params:
3892 data - bytearray or array of int where each int < 255
3893 begin_offset - int offset that should be printed in left column
3894 desc - str optional description to print on the first line to describe data
3895 """
3896 if desc:
3897 print "{}:".format(desc)
3898 index = 0
3899 total_len = len(data)
3900 hex_buf = ""
3901 char_buf = ""
3902 while index < total_len:
3903 hex_buf += " {:02x}".format(data[index])
3904 if data[index] < 0x20 or data[index] > 0x7e:
3905 char_buf += "."
3906 else:
3907 char_buf += "{:c}".format(data[index])
3908 index += 1
3909 if index and index % 8 == 0:
3910 hex_buf += " "
3911 if index > 1 and (index % 16) == 0:
3912 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
3913 hex_buf = ""
3914 char_buf = ""
3915 print "{:08x} {: <50s} |{: <16s}|".format(begin_offset + index - 16, hex_buf, char_buf)
3916 return
3917
3918 @lldb_command('vm_scan_all_pages')
3919 def VMScanAllPages(cmd_args=None):
3920 """Scans the vm_pages[] array
3921 """
3922 vm_pages_count = kern.globals.vm_pages_count
3923 vm_pages = kern.globals.vm_pages
3924
3925 free_count = 0
3926 local_free_count = 0
3927 active_count = 0
3928 local_active_count = 0
3929 inactive_count = 0
3930 speculative_count = 0
3931 throttled_count = 0
3932 wired_count = 0
3933 compressor_count = 0
3934 pageable_internal_count = 0
3935 pageable_external_count = 0
3936 secluded_count = 0
3937 secluded_free_count = 0
3938 secluded_inuse_count = 0
3939
3940 i = 0
3941 while i < vm_pages_count:
3942
3943 if i % 10000 == 0:
3944 print "{:d}/{:d}...\n".format(i,vm_pages_count)
3945
3946 m = vm_pages[i]
3947
3948 internal = 0
3949 external = 0
3950 m_object_val = _vm_page_unpack_ptr(m.vmp_object)
3951
3952 if m_object:
3953 if m_object.internal:
3954 internal = 1
3955 else:
3956 external = 1
3957
3958 if m.vmp_wire_count != 0 and m.vmp_local == 0:
3959 wired_count = wired_count + 1
3960 pageable = 0
3961 elif m.vmp_throttled:
3962 throttled_count = throttled_count + 1
3963 pageable = 0
3964 elif m.vmp_active:
3965 active_count = active_count + 1
3966 pageable = 1
3967 elif m.vmp_local:
3968 local_active_count = local_active_count + 1
3969 pageable = 0
3970 elif m.vmp_inactive:
3971 inactive_count = inactive_count + 1
3972 pageable = 1
3973 elif m.vmp_speculative:
3974 speculative_count = speculative_count + 1
3975 pageable = 0
3976 elif m.vmp_free:
3977 free_count = free_count + 1
3978 pageable = 0
3979 elif m.vmp_secluded:
3980 secluded_count = secluded_count + 1
3981 if m_object == 0:
3982 secluded_free_count = secluded_free_count + 1
3983 else:
3984 secluded_inuse_count = secluded_inuse_count + 1
3985 pageable = 0
3986 elif m_object == 0 and m.vmp_busy:
3987 local_free_count = local_free_count + 1
3988 pageable = 0
3989 elif m.vmp_compressor:
3990 compressor_count = compressor_count + 1
3991 pageable = 0
3992 else:
3993 print "weird page vm_pages[{:d}]?\n".format(i)
3994 pageable = 0
3995
3996 if pageable:
3997 if internal:
3998 pageable_internal_count = pageable_internal_count + 1
3999 else:
4000 pageable_external_count = pageable_external_count + 1
4001 i = i + 1
4002
4003 print "vm_pages_count = {:d}\n".format(vm_pages_count)
4004
4005 print "wired_count = {:d}\n".format(wired_count)
4006 print "throttled_count = {:d}\n".format(throttled_count)
4007 print "active_count = {:d}\n".format(active_count)
4008 print "local_active_count = {:d}\n".format(local_active_count)
4009 print "inactive_count = {:d}\n".format(inactive_count)
4010 print "speculative_count = {:d}\n".format(speculative_count)
4011 print "free_count = {:d}\n".format(free_count)
4012 print "local_free_count = {:d}\n".format(local_free_count)
4013 print "compressor_count = {:d}\n".format(compressor_count)
4014
4015 print "pageable_internal_count = {:d}\n".format(pageable_internal_count)
4016 print "pageable_external_count = {:d}\n".format(pageable_external_count)
4017 print "secluded_count = {:d}\n".format(secluded_count)
4018 print "secluded_free_count = {:d}\n".format(secluded_free_count)
4019 print "secluded_inuse_count = {:d}\n".format(secluded_inuse_count)
4020
4021
4022 @lldb_command('show_all_vm_named_entries')
4023 def ShowAllVMNamedEntries(cmd_args=None):
4024 """ Routine to print a summary listing of all the VM named entries
4025 """
4026 queue_len = kern.globals.vm_named_entry_count
4027 queue_head = kern.globals.vm_named_entry_list
4028
4029 print 'vm_named_entry_list:{: <#018x} vm_named_entry_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('vm_named_entry_list'),queue_len)
4030
4031 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")
4032 idx = 0
4033 for entry in IterateQueue(queue_head, 'struct vm_named_entry *', 'named_entry_list'):
4034 idx += 1
4035 showmemoryentry(entry, idx, queue_len)
4036
4037 @lldb_command('show_vm_named_entry')
4038 def ShowVMNamedEntry(cmd_args=None):
4039 """ Routine to print a VM named entry
4040 """
4041 if cmd_args == None or len(cmd_args) < 1:
4042 print "Invalid argument.", ShowMapVMNamedEntry.__doc__
4043 return
4044 named_entry = kern.GetValueFromAddress(cmd_args[0], 'vm_named_entry_t')
4045 showmemoryentry(named_entry, 0, 0)
4046
4047 def showmemoryentry(entry, idx=0, queue_len=0):
4048 """ Routine to print out a summary a VM memory entry
4049 params:
4050 entry - core.value : a object of type 'struct vm_named_entry *'
4051 returns:
4052 None
4053 """
4054 show_pager_info = True
4055 show_all_shadows = True
4056
4057 backing = ""
4058 if entry.is_sub_map == 1:
4059 backing += "SUBMAP"
4060 if entry.is_copy == 1:
4061 backing += "COPY"
4062 if entry.is_sub_map == 0 and entry.is_copy == 0:
4063 backing += "OBJECT"
4064 prot=""
4065 if entry.protection & 0x1:
4066 prot += "r"
4067 else:
4068 prot += "-"
4069 if entry.protection & 0x2:
4070 prot += "w"
4071 else:
4072 prot += "-"
4073 if entry.protection & 0x4:
4074 prot += "x"
4075 else:
4076 prot += "-"
4077 extra_str = ""
4078 if hasattr(entry, 'named_entry_alias'):
4079 extra_str += " alias={:d}".format(entry.named_entry_alias)
4080 if hasattr(entry, 'named_entry_port'):
4081 extra_str += " port={:#016x}".format(entry.named_entry_port)
4082 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)
4083 if entry.is_sub_map == 1:
4084 showmapvme(entry.backing.map, 0, 0, show_pager_info, show_all_shadows)
4085 if entry.is_copy == 1:
4086 showmapcopyvme(entry.backing.copy, 0, 0, 0, show_pager_info, show_all_shadows, 0)
4087 if entry.is_sub_map == 0 and entry.is_copy == 0:
4088 showvmobject(entry.backing.object, entry.offset, entry.size, show_pager_info, show_all_shadows)
4089
4090
4091 def IterateRBTreeEntry2(element, element_type, field_name1, field_name2):
4092 """ iterate over a rbtree as defined with RB_HEAD in libkern/tree.h
4093 element - value : Value object for rbh_root
4094 element_type - str : Type of the link element
4095 field_name - str : Name of the field in link element's structure
4096 returns:
4097 A generator does not return. It is used for iterating
4098 value : an object thats of type (element_type) head->sle_next. Always a pointer object
4099 """
4100 elt = element.__getattr__('rbh_root')
4101 if type(element_type) == str:
4102 element_type = gettype(element_type)
4103 charp_type = gettype('char *');
4104
4105 # Walk to find min
4106 parent = elt
4107 while unsigned(elt) != 0:
4108 parent = elt
4109 elt = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4110 elt = parent
4111
4112 # Now elt is min
4113 while unsigned(elt) != 0:
4114 yield elt
4115 # implementation cribbed from RB_NEXT in libkern/tree.h
4116 right = cast(elt.__getattr__(field_name1).__getattr__(fieldname2).__getattr__('rbe_right'), element_type)
4117 if unsigned(right) != 0:
4118 elt = right
4119 left = cast(elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4120 while unsigned(left) != 0:
4121 elt = left
4122 left = cast(elt.__getattr__(field_name1).__getattr(__field_name2).__getattr__('rbe_left'), element_type)
4123 else:
4124
4125 # avoid using GetValueFromAddress
4126 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4127 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4128 parent = cast(parent, element_type)
4129
4130 if unsigned(parent) != 0:
4131 left = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_left'), element_type)
4132 if (unsigned(parent) != 0) and (unsigned(elt) == unsigned(left)):
4133 elt = parent
4134 else:
4135 if unsigned(parent) != 0:
4136 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
4137 while unsigned(parent) != 0 and (unsigned(elt) == unsigned(right)):
4138 elt = parent
4139
4140 # avoid using GetValueFromAddress
4141 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4142 parent = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4143 parent = cast(parent, element_type)
4144
4145 right = cast(parent.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_right'), element_type)
4146
4147 # avoid using GetValueFromAddress
4148 addr = elt.__getattr__(field_name1).__getattr__(field_name2).__getattr__('rbe_parent')&~1
4149 elt = value(elt.GetSBValue().CreateValueFromExpression(None,'(void *)'+str(addr)))
4150 elt = cast(elt, element_type)
4151
4152
4153 @lldb_command("showmaprb")
4154 def ShowMapRB(cmd_args=None):
4155 """Routine to print out a VM map's RB tree
4156 usage: showmaprb <vm_map>
4157 """
4158 if cmd_args == None or len(cmd_args) < 1:
4159 print "Invalid argument.", ShowMapRB.__doc__
4160 return
4161 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
4162 print GetVMMapSummary.header
4163 print GetVMMapSummary(map_val)
4164 vme_rb_root = map_val.hdr.rb_head_store
4165 vme_ptr_type = GetType('struct vm_map_entry *')
4166 print GetVMEntrySummary.header
4167 for vme in IterateRBTreeEntry2(vme_rb_root, 'struct vm_map_entry *', 'store', 'entry'):
4168 print GetVMEntrySummary(vme)
4169 return None
4170
4171 @lldb_command('show_all_owned_objects', 'T')
4172 def ShowAllOwnedObjects(cmd_args=None, cmd_options={}):
4173 """ Routine to print the list of VM objects owned by each task
4174 -T: show only ledger-tagged objects
4175 """
4176 showonlytagged = False
4177 if "-T" in cmd_options:
4178 showonlytagged = True
4179 for task in kern.tasks:
4180 ShowTaskOwnedVmObjects(task, showonlytagged)
4181
4182 @lldb_command('show_task_owned_objects', 'T')
4183 def ShowTaskOwnedObjects(cmd_args=None, cmd_options={}):
4184 """ Routine to print the list of VM objects owned by the specified task
4185 -T: show only ledger-tagged objects
4186 """
4187 showonlytagged = False
4188 if "-T" in cmd_options:
4189 showonlytagged = True
4190 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
4191 ShowTaskOwnedVmObjects(task, showonlytagged)
4192
4193 def ShowTaskOwnedVmObjects(task, showonlytagged=False):
4194 """ Routine to print out a summary listing of all the entries in a vm_map
4195 params:
4196 task - core.value : a object of type 'task *'
4197 returns:
4198 None
4199 """
4200 taskobjq_total = lambda:None
4201 taskobjq_total.objects = 0
4202 taskobjq_total.vsize = 0
4203 taskobjq_total.rsize = 0
4204 taskobjq_total.wsize = 0
4205 taskobjq_total.csize = 0
4206 vmo_list_head = task.task_objq
4207 vmo_ptr_type = GetType('vm_object *')
4208 idx = 0
4209 for vmo in IterateQueue(vmo_list_head, vmo_ptr_type, "task_objq"):
4210 idx += 1
4211 if not showonlytagged or vmo.vo_ledger_tag != 0:
4212 if taskobjq_total.objects == 0:
4213 print ' \n'
4214 print GetTaskSummary.header + ' ' + GetProcSummary.header
4215 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
4216 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")
4217 ShowOwnedVmObject(vmo, idx, 0, taskobjq_total)
4218 if taskobjq_total.objects != 0:
4219 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)
4220 return None
4221
4222 def ShowOwnedVmObject(object, idx, queue_len, taskobjq_total):
4223 """ Routine to print out a VM object owned by a task
4224 params:
4225 object - core.value : a object of type 'struct vm_object *'
4226 returns:
4227 None
4228 """
4229 page_size = kern.globals.page_size
4230 if object.purgable == 0:
4231 purgable = "N"
4232 elif object.purgable == 1:
4233 purgable = "V"
4234 elif object.purgable == 2:
4235 purgable = "E"
4236 elif object.purgable == 3:
4237 purgable = "D"
4238 else:
4239 purgable = "?"
4240 if object.pager == 0:
4241 compressed_count = 0
4242 else:
4243 compressor_pager = Cast(object.pager, 'compressor_pager *')
4244 compressed_count = compressor_pager.cpgr_num_slots_occupied
4245
4246 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))
4247
4248 taskobjq_total.objects += 1
4249 taskobjq_total.vsize += object.vo_un1.vou_size/page_size
4250 taskobjq_total.rsize += object.resident_page_count
4251 taskobjq_total.wsize += object.wired_page_count
4252 taskobjq_total.csize += compressed_count
4253
4254 def GetProcPIDForObjectOwner(owner):
4255 """ same as GetProcPIDForTask() but deals with -1 for a disowned object
4256 """
4257 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
4258 return -1
4259 return GetProcPIDForTask(owner)
4260
4261 def GetProcNameForObjectOwner(owner):
4262 """ same as GetProcNameForTask() but deals with -1 for a disowned object
4263 """
4264 if unsigned(Cast(owner, 'int')) == unsigned(int(0xffffffff)):
4265 return "<disowned>"
4266 return GetProcNameForTask(owner)
4267
4268 def GetDescForNamedEntry(mem_entry):
4269 out_str = "\n"
4270 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)
4271 if mem_entry.is_sub_map:
4272 out_str += " is_sub_map"
4273 elif mem_entry.is_copy:
4274 out_str += " is_copy"
4275 else:
4276 out_str += " is_object"
4277 return out_str