]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/memory.py
xnu-4570.20.62.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 (now - phys_footprint_entry._le.le_maxtracking.le_peaks[0].le_time <= 1) and (phys_footprint_entry._le.le_maxtracking.le_peaks[0].le_max > ledger_peak):
64 ledger_peak = phys_footprint_entry._le.le_maxtracking.le_peaks[0].le_max
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 'spike', 'lifemax', 'limit', 'command'))
70 def GetMemoryStatusNode(proc_val):
71 """ Internal function to get memorystatus information from the given proc
72 params: proc - value representing struct proc *
73 return: str - formatted output information for proc object
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_maxtracking.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: zprint
236
237 @lldb_type_summary(['zone','zone_t'])
238 @header("{:^18s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^15s} {:<20s}".format(
239 'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ALL_FREE_PGS', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'WASTE', 'FLAGS', 'NAME'))
240 def GetZoneSummary(zone):
241 """ Summarize a zone with important information. See help zprint for description of each field
242 params:
243 zone: value - obj representing a zone in kernel
244 returns:
245 str - summary of the zone
246 """
247 out_string = ""
248 format_string = '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d} {markings} {name:s} '
249 pagesize = kern.globals.page_size
250
251 free_elements = zone.countfree
252 free_size = free_elements * zone.elem_size
253
254 alloc_pages = zone.alloc_size / pagesize
255 alloc_count = zone.alloc_size / zone.elem_size
256 alloc_waste = zone.alloc_size % zone.elem_size
257
258 marks = [
259 ["collectable", "C"],
260 ["expandable", "X"],
261 ["noencrypt", "$"],
262 ["caller_acct", "@"],
263 ["exhaustible", "H"],
264 ["allows_foreign", "F"],
265 ["async_prio_refill", "R"],
266 ["no_callout", "O"],
267 ["zleak_on", "L"],
268 ["doing_alloc_without_vm_priv", "A"],
269 ["doing_alloc_with_vm_priv", "S"],
270 ["waiting", "W"]
271 ]
272 if kern.arch == 'x86_64':
273 marks.append(["gzalloc_exempt", "M"])
274 marks.append(["alignment_required", "N"])
275
276 markings=""
277 if not zone.__getattr__("zone_valid") :
278 markings+="I"
279 for mark in marks:
280 if zone.__getattr__(mark[0]) :
281 markings+=mark[1]
282 else:
283 markings+=" "
284 out_string += format_string.format(zone, zone.cur_size, zone.page_count,
285 zone.count, free_elements, free_size, zone.count_all_free_pages,
286 zone.elem_size, zone.alloc_size, alloc_count,
287 alloc_pages, alloc_waste, name = zone.zone_name, markings=markings)
288
289 if zone.exhaustible :
290 out_string += "(max: {:d})".format(zone.max_size)
291
292 return out_string
293
294 @lldb_command('zprint')
295 def Zprint(cmd_args=None):
296 """ Routine to print a summary listing of all the kernel zones
297 All columns are printed in decimal
298 Legend:
299 C - collectable
300 X - expandable
301 $ - not encrypted during hibernation
302 @ - allocs and frees are accounted to caller process for KPRVT
303 H - exhaustible
304 F - allows foreign memory (memory not allocated from zone_map)
305 M - gzalloc will avoid monitoring this zone
306 R - will be refilled when below low water mark
307 O - does not allow refill callout to fill zone on noblock allocation
308 N - zone requires alignment (avoids padding this zone for debugging)
309 A - currently trying to allocate more backing memory from kernel_memory_allocate without VM priv
310 S - currently trying to allocate more backing memory from kernel_memory_allocate with VM priv
311 W - another thread is waiting for more memory
312 L - zone is being monitored by zleaks
313 G - currently running GC
314 I - zone was destroyed and is no longer valid
315 """
316 global kern
317 print GetZoneSummary.header
318 for zval in kern.zones:
319 print GetZoneSummary(zval)
320
321 @xnudebug_test('test_zprint')
322 def TestZprint(kernel_target, config, lldb_obj, isConnected ):
323 """ Test the functionality of zprint command
324 returns
325 - False on failure
326 - True on success
327 """
328 if not isConnected:
329 print "Target is not connected. Cannot test memstats"
330 return False
331 res = lldb.SBCommandReturnObject()
332 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res)
333 result = res.GetOutput()
334 if len(result.split("\n")) > 2:
335 return True
336 else:
337 return False
338
339
340 # EndMacro: zprint
341
342 # Macro: showzfreelist
343
344 def ShowZfreeListHeader(zone):
345 """ Helper routine to print a header for zone freelist.
346 (Since the freelist does not have a custom type, this is not defined as a Type Summary).
347 params:
348 zone:zone_t - Zone object to print header info
349 returns:
350 None
351 """
352
353 scaled_factor = (unsigned(kern.globals.zp_factor) +
354 (unsigned(zone.elem_size) >> unsigned(kern.globals.zp_scale)))
355
356 out_str = ""
357 out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
358 out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
359 zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, scaled_factor)
360 out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
361 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
362 print out_str
363
364 def ShowZfreeListChain(zone, zfirst, zlimit):
365 """ Helper routine to print a zone free list chain
366 params:
367 zone: zone_t - Zone object
368 zfirst: void * - A pointer to the first element of the free list chain
369 zlimit: int - Limit for the number of elements to be printed by showzfreelist
370 returns:
371 None
372 """
373 current = Cast(zfirst, 'void *')
374 while ShowZfreeList.elts_found < zlimit:
375 ShowZfreeList.elts_found += 1
376 znext = dereference(Cast(current, 'vm_offset_t *'))
377 znext = (unsigned(znext) ^ unsigned(kern.globals.zp_nopoison_cookie))
378 znext = kern.GetValueFromAddress(znext, 'vm_offset_t *')
379 backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
380 backup_val = dereference(backup_ptr)
381 n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
382 p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
383 poison_str = ''
384 if p_unobfuscated == unsigned(znext):
385 poison_str = "P ({0: <d})".format(ShowZfreeList.elts_found - ShowZfreeList.last_poisoned)
386 ShowZfreeList.last_poisoned = ShowZfreeList.elts_found
387 else:
388 if n_unobfuscated != unsigned(znext):
389 poison_str = "INVALID"
390 print "{0: <7d} 0x{1:0>16x} 0x{2:0>16x} 0x{3:0>16x} 0x{4:0>16x} 0x{5:0>16x} {6: <14s}\n".format(
391 ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
392 if unsigned(znext) == 0:
393 break
394 current = Cast(znext, 'void *')
395
396 @static_var('elts_found',0)
397 @static_var('last_poisoned',0)
398 @lldb_command('showzfreelist')
399 def ShowZfreeList(cmd_args=None):
400 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
401 Usage: showzfreelist <zone> [iterations]
402
403 Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
404 """
405 if not cmd_args:
406 print ShowZfreeList.__doc__
407 return
408 ShowZfreeList.elts_found = 0
409 ShowZfreeList.last_poisoned = 0
410
411 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
412 zlimit = 50
413 if len(cmd_args) >= 2:
414 zlimit = ArgumentStringToInt(cmd_args[1])
415 ShowZfreeListHeader(zone)
416
417 if unsigned(zone.allows_foreign) == 1:
418 for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'):
419 if ShowZfreeList.elts_found == zlimit:
420 break
421 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
422 if unsigned(zfirst) != 0:
423 ShowZfreeListChain(zone, zfirst, zlimit)
424 for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'):
425 if ShowZfreeList.elts_found == zlimit:
426 break
427 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
428 if unsigned(zfirst) != 0:
429 ShowZfreeListChain(zone, zfirst, zlimit)
430 for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
431 if ShowZfreeList.elts_found == zlimit:
432 break
433 zfirst = kern.GetValueFromAddress(GetFreeList(free_page_meta), 'void *')
434 if unsigned(zfirst) != 0:
435 ShowZfreeListChain(zone, zfirst, zlimit)
436
437 if ShowZfreeList.elts_found == zlimit:
438 print "Stopped at {0: <d} elements!".format(zlimit)
439 else:
440 print "Found {0: <d} elements!".format(ShowZfreeList.elts_found)
441
442 # EndMacro: showzfreelist
443
444 # Macro: zstack_showzonesbeinglogged
445
446 @lldb_command('zstack_showzonesbeinglogged')
447 def ZstackShowZonesBeingLogged(cmd_args=None):
448 """ Show all zones which have BTLog enabled.
449 """
450 global kern
451 for zval in kern.zones:
452 if zval.zlog_btlog:
453 print "Zone: %s with its BTLog at: 0x%lx" % (zval.zone_name, zval.zlog_btlog)
454
455 # EndMacro: zstack_showzonesbeinglogged
456
457 # Macro: zstack
458
459 @lldb_command('zstack')
460 def Zstack(cmd_args=None):
461 """ Zone leak debugging: Print the stack trace logged at <index> in the stacks list. If a <count> is supplied, it prints <count> stacks starting at <index>.
462 Usage: zstack <btlog addr> <index> [<count>]
463
464 The suggested usage is to look at stacks with high percentage of refs (maybe > 25%).
465 The stack trace that occurs the most is probably the cause of the leak. Use zstack_findleak for that.
466 """
467 if not cmd_args:
468 print Zstack.__doc__
469 return
470 if int(kern.globals.log_records) == 0:
471 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
472 return
473
474 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
475 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
476 btrecord_size = unsigned(btlog_ptr.btrecord_size)
477 btrecords = unsigned(btlog_ptr.btrecords)
478 btlog_size = unsigned(sizeof('struct btlog'))
479 depth = unsigned(btlog_ptr.btrecord_btdepth)
480 zstack_index = ArgumentStringToInt(cmd_args[1])
481 count = 1
482 if len(cmd_args) >= 3:
483 count = ArgumentStringToInt(cmd_args[2])
484
485 max_count = ((btrecords_total_size - btlog_size)/btrecord_size)
486
487 if (zstack_index + count) > max_count:
488 count = max_count - zstack_index
489
490 while count and (zstack_index != 0xffffff):
491 zstack_record_offset = zstack_index * btrecord_size
492 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
493 if int(zstack_record.ref_count)!=0:
494 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
495 zstack_index += 1
496 count -= 1
497
498 # EndMacro : zstack
499
500 # Macro: zstack_inorder
501
502 @lldb_command('zstack_inorder')
503 def ZstackInOrder(cmd_args=None):
504 """ Zone leak debugging: Print the stack traces starting from head to the tail.
505 Usage: zstack_inorder <btlog addr>
506 """
507 if not cmd_args:
508 print "Zone leak debugging: Print the stack traces starting from head to the tail. \nUsage: zstack_inorder <btlog addr>"
509 return
510 if int(kern.globals.log_records) == 0:
511 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
512 return
513
514 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
515 btrecords_total_size = unsigned(btlog_ptr.btlog_buffersize)
516 btrecord_size = unsigned(btlog_ptr.btrecord_size)
517 btrecords = unsigned(btlog_ptr.btrecords)
518 btlog_size = unsigned(sizeof('struct btlog'))
519 depth = unsigned(btlog_ptr.btrecord_btdepth)
520 zstack_head = unsigned(btlog_ptr.head)
521 zstack_index = zstack_head
522 zstack_tail = unsigned(btlog_ptr.tail)
523 count = ((btrecords_total_size - btlog_size)/btrecord_size)
524
525 while count and (zstack_index != 0xffffff):
526 zstack_record_offset = zstack_index * btrecord_size
527 zstack_record = kern.GetValueFromAddress(btrecords + zstack_record_offset, 'btlog_record_t *')
528 ShowZStackRecord(zstack_record, zstack_index, depth, unsigned(btlog_ptr.active_element_count))
529 zstack_index = zstack_record.next
530 count -= 1
531
532 # EndMacro : zstack_inorder
533
534 # Macro: findoldest
535
536 @lldb_command('findoldest')
537 def FindOldest(cmd_args=None):
538 """
539 """
540 print "***** DEPRECATED ***** use 'zstack_findleak' macro instead."
541 return
542 # EndMacro : findoldest
543
544 # Macro : zstack_findleak
545
546 @lldb_command('zstack_findleak')
547 def zstack_findleak(cmd_args=None):
548 """ Zone leak debugging: search the log and print the stack with the most active references
549 in the stack trace.
550 Usage: zstack_findleak <btlog address>
551
552 This is useful for verifying a suspected stack as being the source of
553 the leak.
554 """
555 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
556 btrecord_size = unsigned(btlog_ptr.btrecord_size)
557 btrecords = unsigned(btlog_ptr.btrecords)
558
559 cpcs_index = unsigned(btlog_ptr.head)
560 depth = unsigned(btlog_ptr.btrecord_btdepth)
561 highref = 0
562 highref_index = 0
563 highref_record = 0
564
565 while cpcs_index != 0xffffff:
566 cpcs_record_offset = cpcs_index * btrecord_size
567 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
568 if cpcs_record.ref_count > highref:
569 highref_record = cpcs_record
570 highref = cpcs_record.ref_count
571 highref_index = cpcs_index
572 cpcs_index = cpcs_record.next
573 ShowZStackRecord(highref_record, highref_index, depth, unsigned(btlog_ptr.active_element_count))
574
575 # EndMacro: zstack_findleak
576
577 # Macro: findelem
578
579 @lldb_command('findelem')
580 def FindElem(cmd_args=None):
581 """
582 """
583 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
584 return
585 # EndMacro: findelem
586
587 @lldb_command('zstack_findelem')
588 def ZStackFindElem(cmd_args=None):
589 """ Zone corruption debugging: search the zone log and print out the stack traces for all log entries that
590 refer to the given zone element.
591 Usage: zstack_findelem <btlog addr> <elem addr>
592
593 When the kernel panics due to a corrupted zone element, get the
594 element address and use this command. This will show you the stack traces of all logged zalloc and
595 zfree operations which tells you who touched the element in the recent past. This also makes
596 double-frees readily apparent.
597 """
598 if not cmd_args:
599 print ZStackFindElem.__doc__
600 return
601 if int(kern.globals.log_records) == 0 or unsigned(kern.globals.corruption_debug_flag) == 0:
602 print "Zone logging with corruption detection not enabled. Add '-zc zlog=<zone name>' to boot-args."
603 return
604
605 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
606 target_element = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
607
608 btrecord_size = unsigned(btlog_ptr.btrecord_size)
609 btrecords = unsigned(btlog_ptr.btrecords)
610 depth = unsigned(btlog_ptr.btrecord_btdepth)
611
612 prev_op = -1
613 scan_items = 0
614 hashelem = cast(btlog_ptr.elem_linkage_un.element_hash_queue.tqh_first, 'btlog_element_t *')
615 if (target_element >> 32) != 0:
616 target_element = target_element ^ 0xFFFFFFFFFFFFFFFF
617 else:
618 target_element = target_element ^ 0xFFFFFFFF
619 while hashelem != 0:
620 if unsigned(hashelem.elem) == target_element:
621 recindex = hashelem.recindex
622 recoffset = recindex * btrecord_size
623 record = kern.GetValueFromAddress(btrecords + recoffset, 'btlog_record_t *')
624 out_str = ('-' * 8)
625 if record.operation == 1:
626 out_str += "OP: ALLOC. "
627 else:
628 out_str += "OP: FREE. "
629 out_str += "Stack Index {0: <d} {1: <s}\n".format(recindex, ('-' * 8))
630 print out_str
631 print GetBtlogBacktrace(depth, record)
632 print " \n"
633 if int(record.operation) == prev_op:
634 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
635 return
636 prev_op = int(record.operation)
637 scan_items = 0
638 hashelem = cast(hashelem.element_hash_link.tqe_next, 'btlog_element_t *')
639 scan_items += 1
640 if scan_items % 100 == 0:
641 print "Scanning is ongoing. {0: <d} items scanned since last check." .format(scan_items)
642
643 # EndMacro: zstack_findelem
644
645 @lldb_command('zstack_findtop', 'N:')
646 def ShowZstackTop(cmd_args=None, cmd_options={}):
647 """ Zone leak debugging: search the log and print the stacks with the most active references
648 in the stack trace.
649
650 Usage: zstack_findtop [-N <n-stacks>] <btlog-addr>
651 """
652
653 if not cmd_args:
654 raise ArgumentError('Missing required btlog address argument')
655
656 n = 5
657 if '-N' in cmd_options:
658 n = int(cmd_options['-N'])
659
660 btlog_ptr = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
661 btrecord_size = unsigned(btlog_ptr.btrecord_size)
662 btrecords = unsigned(btlog_ptr.btrecords)
663
664 cpcs_index = unsigned(btlog_ptr.head)
665 depth = unsigned(btlog_ptr.btrecord_btdepth)
666
667 records = []
668 while cpcs_index != 0xffffff:
669 cpcs_record_offset = cpcs_index * btrecord_size
670 cpcs_record = kern.GetValueFromAddress(btrecords + cpcs_record_offset, 'btlog_record_t *')
671 cpcs_record.index = cpcs_index
672 records.append(cpcs_record)
673 cpcs_index = cpcs_record.next
674
675 recs = sorted(records, key=lambda x: x.ref_count, reverse=True)
676
677 for rec in recs[:n]:
678 ShowZStackRecord(rec, rec.index, depth, unsigned(btlog_ptr.active_element_count))
679
680 # EndMacro: zstack_findtop
681
682 # Macro: btlog_find
683
684 @lldb_command('btlog_find', "AS")
685 def BtlogFind(cmd_args=None, cmd_options={}):
686 """
687 """
688 print "***** DEPRECATED ***** use 'zstack_findelem' macro instead."
689 return
690
691 #EndMacro: btlog_find
692
693 #Macro: showzalloc
694
695 @lldb_command('showzalloc')
696 def ShowZalloc(cmd_args=None):
697 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
698 Usage: showzalloc <index>
699 """
700 if not cmd_args:
701 print ShowZalloc.__doc__
702 return
703 if unsigned(kern.globals.zallocations) == 0:
704 print "zallocations array not initialized!"
705 return
706 zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])]
707 print zallocation
708 ShowZTrace([str(int(zallocation.za_trace_index))])
709
710 #EndMacro: showzalloc
711
712 #Macro: showztrace
713
714 @lldb_command('showztrace')
715 def ShowZTrace(cmd_args=None):
716 """ Prints the backtrace from the ztraces array at index
717 Usage: showztrace <trace index>
718 """
719 if not cmd_args:
720 print ShowZTrace.__doc__
721 return
722 if unsigned(kern.globals.ztraces) == 0:
723 print "ztraces array not initialized!"
724 return
725 ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])]
726 print ztrace_addr
727 ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth)
728
729 #EndMacro: showztrace
730
731 #Macro: showztraceaddr
732
733 @lldb_command('showztraceaddr')
734 def ShowZTraceAddr(cmd_args=None):
735 """ Prints the struct ztrace passed in.
736 Usage: showztraceaddr <trace address>
737 """
738 if not cmd_args:
739 print ShowZTraceAddr.__doc__
740 return
741 ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *')
742 print dereference(ztrace_ptr)
743 ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth)
744
745 #EndMacro: showztraceaddr
746
747 #Macro: showzstacktrace
748
749 @lldb_command('showzstacktrace')
750 def ShowZstackTrace(cmd_args=None):
751 """ Routine to print a stacktrace stored by OSBacktrace.
752 Usage: showzstacktrace <saved stacktrace> [size]
753
754 size is optional, defaults to 15.
755 """
756 if not cmd_args:
757 print ShowZstackTrace.__doc__
758 return
759 void_ptr_type = gettype('void *')
760 void_double_ptr_type = void_ptr_type.GetPointerType()
761 trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type)
762 trace_size = 15
763 if len(cmd_args) >= 2:
764 trace_size = ArgumentStringToInt(cmd_args[1])
765 ShowZstackTraceHelper(trace, trace_size)
766
767 #EndMacro: showzstacktrace
768
769 def ShowZstackTraceHelper(stack, depth):
770 """ Helper routine for printing a zstack.
771 params:
772 stack: void *[] - An array of pointers representing the Zstack
773 depth: int - The depth of the ztrace stack
774 returns:
775 None
776 """
777 trace_current = 0
778 while trace_current < depth:
779 trace_addr = stack[trace_current]
780 symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr))
781 if symbol_arr:
782 symbol_str = str(symbol_arr[0].addr)
783 else:
784 symbol_str = ''
785 print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str)
786 trace_current += 1
787
788 #Macro: showtopztrace
789
790 @lldb_command('showtopztrace')
791 def ShowTopZtrace(cmd_args=None):
792 """ Shows the ztrace with the biggest size.
793 (According to top_ztrace, not by iterating through the hash table)
794 """
795 top_trace = kern.globals.top_ztrace
796 print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace'))
797 print dereference(top_trace)
798 ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth)
799
800 #EndMacro: showtopztrace
801
802 #Macro: showzallocs
803
804 @lldb_command('showzallocs')
805 def ShowZallocs(cmd_args=None):
806 """ Prints all allocations in the zallocations table
807 """
808 if unsigned(kern.globals.zallocations) == 0:
809 print "zallocations array not initialized!"
810 return
811 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
812 current_index = 0
813 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
814 allocation_count = 0
815 while current_index < max_zallocation:
816 current_zalloc = kern.globals.zallocations[current_index]
817 if int(current_zalloc.za_element) != 0:
818 print '{0: <5d} {1: <#018x} {2: <5d} {3: <15d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_trace_index, unsigned(current_zalloc.za_size))
819 allocation_count += 1
820 current_index += 1
821 print 'Total Allocations: {0: <d}'.format(allocation_count)
822
823 #EndMacro: showzallocs
824
825 #Macro: showzallocsfortrace
826
827 @lldb_command('showzallocsfortrace')
828 def ShowZallocsForTrace(cmd_args=None):
829 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
830 Usage: showzallocsfortrace <trace index>
831 """
832 if not cmd_args:
833 print ShowZallocsForTrace.__doc__
834 return
835 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
836 target_index = ArgumentStringToInt(cmd_args[0])
837 current_index = 0
838 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
839 allocation_count = 0
840 while current_index < max_zallocation:
841 current_zalloc = kern.globals.zallocations[current_index]
842 if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)):
843 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size)
844 allocation_count += 1
845 current_index += 1
846 print 'Total Allocations: {0: <d}'.format(allocation_count)
847
848 #EndMacro: showzallocsfortrace
849
850 #Macro: showztraces
851
852 @lldb_command('showztraces')
853 def ShowZTraces(cmd_args=None):
854 """ Prints all traces with size > 0
855 """
856 ShowZTracesAbove([0])
857
858 #EndMacro: showztraces
859
860 #Macro: showztracesabove
861
862 @lldb_command('showztracesabove')
863 def ShowZTracesAbove(cmd_args=None):
864 """ Prints all traces with size greater than X
865 Usage: showztracesabove <size>
866 """
867 if not cmd_args:
868 print ShowZTracesAbove.__doc__
869 return
870 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
871 current_index = 0
872 ztrace_count = 0
873 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
874 while current_index < max_ztrace:
875 ztrace_current = kern.globals.ztraces[current_index]
876 if ztrace_current.zt_size > unsigned(cmd_args[0]):
877 print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size))
878 ztrace_count += 1
879 current_index += 1
880 print 'Total traces: {0: <d}'.format(ztrace_count)
881
882 #EndMacro: showztracesabove
883
884 #Macro: showztracehistogram
885
886 @lldb_command('showztracehistogram')
887 def ShowZtraceHistogram(cmd_args=None):
888 """ Prints the histogram of the ztrace table
889 """
890 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
891 current_index = 0
892 ztrace_count = 0
893 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
894 while current_index < max_ztrace:
895 ztrace_current = kern.globals.ztraces[current_index]
896 if ztrace_current.zt_hit_count != 0:
897 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions)
898 ztrace_count += 1
899 current_index += 1
900 print 'Total traces: {0: <d}'.format(ztrace_count)
901
902 #EndMacro: showztracehistogram
903
904 #Macro: showzallochistogram
905
906 @lldb_command('showzallochistogram')
907 def ShowZallocHistogram(cmd_args=None):
908 """ Prints the histogram for the zalloc table
909 """
910 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
911 current_index = 0
912 zallocation_count = 0
913 max_ztrace = unsigned(kern.globals.zleak_alloc_buckets)
914 while current_index < max_ztrace:
915 zallocation_current = kern.globals.zallocations[current_index]
916 if zallocation_current.za_hit_count != 0:
917 print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count)
918 zallocation_count += 1
919 current_index += 1
920 print 'Total Allocations: {0: <d}'.format(zallocation_count)
921
922 #EndMacro: showzallochistogram
923
924 #Macro: showzstats
925
926 @lldb_command('showzstats')
927 def ShowZstats(cmd_args=None):
928 """ Prints the zone leak detection stats
929 """
930 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions))
931 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites))
932 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded))
933
934 #EndMacro: showzstats
935
936 def GetBtlogBacktrace(depth, zstack_record):
937 """ Helper routine for getting a BT Log record backtrace stack.
938 params:
939 depth:int - The depth of the zstack record
940 zstack_record:btlog_record_t * - A BTLog record
941 returns:
942 str - string with backtrace in it.
943 """
944 out_str = ''
945 frame = 0
946 if not zstack_record:
947 return "Zstack record none!"
948
949 depth_val = unsigned(depth)
950 while frame < depth_val:
951 frame_pc = zstack_record.bt[frame]
952 if not frame_pc or int(frame_pc) == 0:
953 break
954 symbol_arr = kern.SymbolicateFromAddress(frame_pc)
955 if symbol_arr:
956 symbol_str = str(symbol_arr[0].addr)
957 else:
958 symbol_str = ''
959 out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
960 frame += 1
961 return out_str
962
963 def ShowZStackRecord(zstack_record, zstack_index, btrecord_btdepth, elements_count):
964 """ Helper routine for printing a single zstack record
965 params:
966 zstack_record:btlog_record_t * - A BTLog record
967 zstack_index:int - Index for the record in the BTLog table
968 returns:
969 None
970 """
971 out_str = ('-' * 8)
972 if zstack_record.operation == 1:
973 out_str += "ALLOC. "
974 else:
975 out_str += "FREE. "
976 out_str += "Stack Index {0: <d} with active refs {1: <d} of {2: <d} {3: <s}\n".format(zstack_index, zstack_record.ref_count, elements_count, ('-' * 8))
977 print out_str
978 print GetBtlogBacktrace(btrecord_btdepth, zstack_record)
979 print " \n"
980
981 # Macro: showioalloc
982
983 @lldb_command('showioalloc')
984 def ShowIOAllocations(cmd_args=None):
985 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
986 Routine to display a summary of memory accounting allocated by IOKit allocators.
987 """
988 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024))
989 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
990 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
991 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
992
993
994 # EndMacro: showioalloc
995
996
997 # Macro: showselectmem
998 @lldb_command('showselectmem', "S:")
999 def ShowSelectMem(cmd_args=None, cmd_options={}):
1000 """ Show memory cached by threads on calls to select.
1001
1002 usage: showselectmem [-v]
1003 -v : print each thread's memory
1004 (one line per thread with non-zero select memory)
1005 -S {addr} : Find the thread whose thread-local select set
1006 matches the given address
1007 """
1008 verbose = False
1009 opt_wqs = 0
1010 if config['verbosity'] > vHUMAN:
1011 verbose = True
1012 if "-S" in cmd_options:
1013 opt_wqs = unsigned(kern.GetValueFromAddress(cmd_options["-S"], 'uint64_t *'))
1014 if opt_wqs == 0:
1015 raise ArgumentError("Invalid waitq set address: {:s}".format(cmd_options["-S"]))
1016 selmem = 0
1017 if verbose:
1018 print "{:18s} {:10s} {:s}".format('Task', 'Thread ID', 'Select Mem (bytes)')
1019 for t in kern.tasks:
1020 for th in IterateQueue(t.threads, 'thread *', 'task_threads'):
1021 uth = Cast(th.uthread, 'uthread *');
1022 wqs = 0
1023 if hasattr(uth, 'uu_allocsize'): # old style
1024 thmem = uth.uu_allocsize
1025 wqs = uth.uu_wqset
1026 elif hasattr(uth, 'uu_wqstate_sz'): # new style
1027 thmem = uth.uu_wqstate_sz
1028 wqs = uth.uu_wqset
1029 else:
1030 print "What kind of uthread is this?!"
1031 return
1032 if opt_wqs and opt_wqs == unsigned(wqs):
1033 print "FOUND: {:#x} in thread: {:#x} ({:#x})".format(opt_wqs, unsigned(th), unsigned(th.thread_id))
1034 if verbose and thmem > 0:
1035 print "{:<#18x} {:<#10x} {:d}".format(unsigned(t), unsigned(th.thread_id), thmem)
1036 selmem += thmem
1037 print '-'*40
1038 print "Total: {:d} bytes ({:d} kbytes)".format(selmem, selmem/1024)
1039 # Endmacro: showselectmem
1040
1041
1042 # Macro: showtaskvme
1043 @lldb_command('showtaskvme', "PS")
1044 def ShowTaskVmeHelper(cmd_args=None, cmd_options={}):
1045 """ Display a summary list of the specified vm_map's entries
1046 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
1047 Use -S flag to show VM object shadow chains
1048 Use -P flag to show pager info (mapped file, compressed pages, ...)
1049 """
1050 show_pager_info = False
1051 show_all_shadows = False
1052 if "-P" in cmd_options:
1053 show_pager_info = True
1054 if "-S" in cmd_options:
1055 show_all_shadows = True
1056 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
1057 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
1058
1059 @lldb_command('showallvme', "PS")
1060 def ShowAllVME(cmd_args=None, cmd_options={}):
1061 """ Routine to print a summary listing of all the vm map entries
1062 Go Through each task in system and show the vm memory regions
1063 Use -S flag to show VM object shadow chains
1064 Use -P flag to show pager info (mapped file, compressed pages, ...)
1065 """
1066 show_pager_info = False
1067 show_all_shadows = False
1068 if "-P" in cmd_options:
1069 show_pager_info = True
1070 if "-S" in cmd_options:
1071 show_all_shadows = True
1072 for task in kern.tasks:
1073 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
1074
1075 @lldb_command('showallvm')
1076 def ShowAllVM(cmd_args=None):
1077 """ Routine to print a summary listing of all the vm maps
1078 """
1079 for task in kern.tasks:
1080 print GetTaskSummary.header + ' ' + GetProcSummary.header
1081 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1082 print GetVMMapSummary.header
1083 print GetVMMapSummary(task.map)
1084
1085 @lldb_command("showtaskvm")
1086 def ShowTaskVM(cmd_args=None):
1087 """ Display info about the specified task's vm_map
1088 syntax: (lldb) showtaskvm <task_ptr>
1089 """
1090 if not cmd_args:
1091 print ShowTaskVM.__doc__
1092 return False
1093 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
1094 if not task:
1095 print "Unknown arguments."
1096 return False
1097 print GetTaskSummary.header + ' ' + GetProcSummary.header
1098 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
1099 print GetVMMapSummary.header
1100 print GetVMMapSummary(task.map)
1101 return True
1102
1103 @lldb_command('showallvmstats')
1104 def ShowAllVMStats(cmd_args=None):
1105 """ Print a summary of vm statistics in a table format
1106 """
1107 page_size = kern.globals.page_size
1108 vmstats = lambda:None
1109 vmstats.wired_count = 0
1110 vmstats.resident_count = 0
1111 vmstats.resident_max = 0
1112 vmstats.internal = 0
1113 vmstats.external = 0
1114 vmstats.reusable = 0
1115 vmstats.compressed = 0
1116 vmstats.compressed_peak = 0
1117 vmstats.compressed_lifetime = 0
1118 vmstats.error = ''
1119
1120 hdr_format = "{0: >10s} {1: <20s} {2: >6s} {3: >10s} {4: >10s} {5: >10s} {6: >10s} {7: >10s} {8: >10s} {9: >10s} {10: >10s} {11: >10s} {12: >10s} {13: >10s} {14:}"
1121 print hdr_format.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '')
1122 print hdr_format.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '')
1123 entry_format = "{p.p_pid: >10d} {p.p_comm: <20s} {m.hdr.nentries: >6d} {s.wired_count: >10d} {vsize: >10d} {s.resident_count: >10d} {s.new_resident_count: >10d} {s.resident_max: >10d} {s.internal: >10d} {s.external: >10d} {s.reusable: >10d} {s.compressed: >10d} {s.compressed_peak: >10d} {s.compressed_lifetime: >10d} {s.error}"
1124
1125 for task in kern.tasks:
1126 proc = Cast(task.bsd_info, 'proc *')
1127 vmmap = Cast(task.map, '_vm_map *')
1128 vmstats.error = ''
1129 vmstats.wired_count = vmmap.pmap.stats.wired_count;
1130 vmstats.resident_count = unsigned(vmmap.pmap.stats.resident_count);
1131 vmstats.resident_max = vmmap.pmap.stats.resident_max;
1132 vmstats.internal = unsigned(vmmap.pmap.stats.internal);
1133 vmstats.external = unsigned(vmmap.pmap.stats.external);
1134 vmstats.reusable = unsigned(vmmap.pmap.stats.reusable);
1135 vmstats.compressed = unsigned(vmmap.pmap.stats.compressed);
1136 vmstats.compressed_peak = unsigned(vmmap.pmap.stats.compressed_peak);
1137 vmstats.compressed_lifetime = unsigned(vmmap.pmap.stats.compressed_lifetime);
1138 vmstats.new_resident_count = vmstats.internal + vmstats.external
1139
1140 if vmstats.internal < 0:
1141 vmstats.error += '*'
1142 if vmstats.external < 0:
1143 vmstats.error += '*'
1144 if vmstats.reusable < 0:
1145 vmstats.error += '*'
1146 if vmstats.compressed < 0:
1147 vmstats.error += '*'
1148 if vmstats.compressed_peak < 0:
1149 vmstats.error += '*'
1150 if vmstats.compressed_lifetime < 0:
1151 vmstats.error += '*'
1152 if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
1153 vmstats.error += '*'
1154
1155 print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) / page_size), t=task, s=vmstats)
1156
1157
1158 def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
1159 """ Routine to print out a summary listing of all the entries in a vm_map
1160 params:
1161 task - core.value : a object of type 'task *'
1162 returns:
1163 None
1164 """
1165 print "vm_map entries for task " + hex(task)
1166 print GetTaskSummary.header
1167 print GetTaskSummary(task)
1168 if not task.map:
1169 print "Task {0: <#020x} has map = 0x0"
1170 return None
1171 print GetVMMapSummary.header
1172 print GetVMMapSummary(task.map)
1173 vme_list_head = task.map.hdr.links
1174 vme_ptr_type = GetType('vm_map_entry *')
1175 print GetVMEntrySummary.header
1176 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1177 print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
1178 return None
1179
1180 @lldb_command("showmap")
1181 def ShowMap(cmd_args=None):
1182 """ Routine to print out info about the specified vm_map
1183 usage: showmap <vm_map>
1184 """
1185 if cmd_args == None or len(cmd_args) < 1:
1186 print "Invalid argument.", ShowMap.__doc__
1187 return
1188 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1189 print GetVMMapSummary.header
1190 print GetVMMapSummary(map_val)
1191
1192 @lldb_command("showmapvme")
1193 def ShowMapVME(cmd_args=None):
1194 """Routine to print out info about the specified vm_map and its vm entries
1195 usage: showmapvme <vm_map>
1196 """
1197 if cmd_args == None or len(cmd_args) < 1:
1198 print "Invalid argument.", ShowMap.__doc__
1199 return
1200 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1201 print GetVMMapSummary.header
1202 print GetVMMapSummary(map_val)
1203 vme_list_head = map_val.hdr.links
1204 vme_ptr_type = GetType('vm_map_entry *')
1205 print GetVMEntrySummary.header
1206 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1207 print GetVMEntrySummary(vme)
1208 return None
1209
1210 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
1211 @header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: >5s} {5: <20s} {6: <20s}".format("vm_map", "pmap", "vm_size", "#ents", "rpage", "hint", "first_free"))
1212 def GetVMMapSummary(vmmap):
1213 """ Display interesting bits from vm_map struct """
1214 out_string = ""
1215 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
1216 vm_size = uint64_t(vmmap.size).value
1217 resident_pages = 0
1218 if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
1219 first_free = 0
1220 if int(vmmap.holelistenabled) == 0: first_free = vmmap.f_s.first_free
1221 out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, first_free)
1222 return out_string
1223
1224 @lldb_type_summary(['vm_map_entry'])
1225 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1226 def GetVMEntrySummary(vme):
1227 """ Display vm entry specific information. """
1228 page_size = kern.globals.page_size
1229 out_string = ""
1230 format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1231 vme_protection = int(vme.protection)
1232 vme_max_protection = int(vme.max_protection)
1233 vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
1234 if int(vme.is_sub_map) != 0 :
1235 vme_extra_info_str +="s"
1236 elif int(vme.needs_copy) != 0 :
1237 vme_extra_info_str +="n"
1238 num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) / page_size
1239 out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.vme_object.vmo_object, vme.vme_offset)
1240 return out_string
1241
1242 # EndMacro: showtaskvme
1243 @lldb_command('showmapwired')
1244 def ShowMapWired(cmd_args=None):
1245 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1246 """
1247 if cmd_args == None or len(cmd_args) < 1:
1248 print "Invalid argument", ShowMapWired.__doc__
1249 return
1250 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1251
1252
1253 @lldb_type_summary(['kmod_info_t *'])
1254 @header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} {7: >20s} {8: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size', 'version', 'name'))
1255 def GetKextSummary(kmod):
1256 """ returns a string representation of kext information
1257 """
1258 out_string = ""
1259 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: <#020x} {6: <#020x} {7: >20s} {8: <30s}"
1260 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(kmod.address), unsigned(kmod.size))
1261 text_segment = macho.get_text_segment(segments)
1262 if not text_segment:
1263 text_segment = segments[0]
1264 out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, text_segment.vmaddr, text_segment.vmsize, kmod.version, kmod.name)
1265 return out_string
1266
1267 @lldb_type_summary(['uuid_t'])
1268 @header("")
1269 def GetUUIDSummary(uuid):
1270 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1271 """
1272 arr = Cast(addressof(uuid), 'uint8_t *')
1273 data = []
1274 for i in range(16):
1275 data.append(int(arr[i]))
1276 return "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=data)
1277
1278 @lldb_command('showallkmods')
1279 def ShowAllKexts(cmd_args=None):
1280 """Display a summary listing of all loaded kexts (alias: showallkmods)
1281 """
1282 kmod_val = kern.globals.kmod
1283 kextuuidinfo = GetKextLoadInformation(show_progress=(config['verbosity'] > vHUMAN))
1284 print "{: <36s} ".format("UUID") + GetKextSummary.header
1285 for kval in IterateLinkedList(kmod_val, 'next'):
1286 uuid = "........-....-....-....-............"
1287 kaddr = unsigned(kval.address)
1288 found_kext_summary = None
1289 for l in kextuuidinfo :
1290 if kaddr == int(l[3],16):
1291 uuid = l[0]
1292 found_kext_summary = l
1293 break
1294 if found_kext_summary:
1295 _ksummary = GetKextSummary(found_kext_summary[7])
1296 else:
1297 _ksummary = GetKextSummary(kval)
1298 print uuid + " " + _ksummary
1299
1300 def GetKmodWithAddr(addr):
1301 """ Go through kmod list and find one with begin_addr as addr
1302 returns: None if not found. else a cvalue of type kmod
1303 """
1304 kmod_val = kern.globals.kmod
1305 for kval in IterateLinkedList(kmod_val, 'next'):
1306 if addr == unsigned(kval.address):
1307 return kval
1308 return None
1309
1310 def GetAllSegmentsAndSectionsFromDataInMemory(address, size):
1311 """ reads memory at address and parses mach_header to get segment and section information
1312 returns: Tuple of (segments_list, sections_list) like ([MachOSegment,...], [MachOSegment, ...])
1313 where MachOSegment has fields like 'name vmaddr vmsize fileoff filesize'
1314 if TEXT segment is not found a dummy segment & section with address, size is returned.
1315 """
1316 cache_hash = "kern.kexts.segments.{}.{}".format(address, size)
1317 cached_result = caching.GetDynamicCacheData(cache_hash,())
1318 if cached_result:
1319 return cached_result
1320
1321 defval = macho.MachOSegment('__TEXT', address, size, 0, size)
1322 if address == 0 or size == 0:
1323 return ([defval], [defval])
1324
1325 ## if int(kern.globals.gLoadedKextSummaries.version) <= 2:
1326 # until we have separate version. we will pay penalty only on arm64 devices
1327 if not kern.arch.startswith('arm64'):
1328 return ([defval], [defval])
1329
1330 restrict_size_to_read = 1536
1331 machoObject = None
1332 while machoObject is None:
1333 err = lldb.SBError()
1334 size_to_read = min(size, restrict_size_to_read)
1335 data = LazyTarget.GetProcess().ReadMemory(address, size_to_read, err)
1336 if not err.Success():
1337 print "Failed to read memory at {} and size {}".format(address, size_to_read)
1338 return ([defval], [defval])
1339 try:
1340 m = macho.MemMacho(data, len(data))
1341 machoObject = m
1342 except Exception as e:
1343 if str(e.message).find('unpack requires a string argument') >= 0:
1344 # this may be due to short read of memory. Lets do double read size.
1345 restrict_size_to_read *= 2
1346 debuglog("Bumping mach header read size to {}".format(restrict_size_to_read))
1347 continue
1348 else:
1349 print "Failed to read MachO for address {} errormessage: {}".format(address, e.message)
1350 return ([defval], [defval])
1351 # end of while loop. We have machoObject defined
1352 segments = machoObject.get_segments_with_name('')
1353 sections = machoObject.get_sections_with_name('')
1354 rval = (segments, sections)
1355 caching.SaveDynamicCacheData(cache_hash, rval)
1356 return rval
1357
1358 def GetKextLoadInformation(addr=0, show_progress=False):
1359 """ Extract the kext uuid and load address information from the kernel data structure.
1360 params:
1361 addr - int - optional integer that is the address to search for.
1362 returns:
1363 [] - array with each entry of format
1364 ( 'UUID', 'Hex Load Address of __TEXT or __TEXT_EXEC section', 'name',
1365 'addr of macho header', [macho.MachOSegment,..], [MachoSection,...], kext, kmod_obj)
1366 """
1367 cached_result = caching.GetDynamicCacheData("kern.kexts.loadinformation", [])
1368 ## if specific addr is provided then ignore caching
1369 if cached_result and not addr:
1370 return cached_result
1371
1372 # because of <rdar://problem/12683084>, we can't find summaries directly
1373 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1374 baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
1375 summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
1376 total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries)
1377 kext_version = int(kern.globals.gLoadedKextSummaries.version)
1378 entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4
1379 if kext_version >= 2 :
1380 entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
1381 retval = []
1382 for i in range(total_summaries):
1383 if show_progress:
1384 print "progress: {}/{}".format(i, total_summaries)
1385 tmpaddress = unsigned(summaries_begin) + (i * entry_size)
1386 current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
1387 # code to extract macho information
1388 segments, sections = GetAllSegmentsAndSectionsFromDataInMemory(unsigned(current_kext.address), unsigned(current_kext.size))
1389 seginfo = macho.get_text_segment(segments)
1390 if not seginfo:
1391 seginfo = segments[0]
1392 kmod_obj = GetKmodWithAddr(unsigned(current_kext.address))
1393 if addr != 0 :
1394 if addr == unsigned(current_kext.address) or addr == seginfo.vmaddr:
1395 return [(GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj)]
1396 retval.append((GetUUIDSummary(current_kext.uuid) , hex(seginfo.vmaddr).rstrip('L'), str(current_kext.name), hex(current_kext.address), segments, seginfo, current_kext, kmod_obj))
1397
1398 if not addr:
1399 caching.SaveDynamicCacheData("kern.kexts.loadinformation", retval)
1400 return retval
1401
1402 lldb_alias('showallkexts', 'showallkmods')
1403
1404 def GetOSKextVersion(version_num):
1405 """ returns a string of format 1.2.3x from the version_num
1406 params: version_num - int
1407 return: str
1408 """
1409 if version_num == -1 :
1410 return "invalid"
1411 (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
1412 version = version_num
1413
1414 vers_major = version / MAJ_MULT
1415 version = version - (vers_major * MAJ_MULT)
1416
1417 vers_minor = version / MIN_MULT
1418 version = version - (vers_minor * MIN_MULT)
1419
1420 vers_revision = version / REV_MULT
1421 version = version - (vers_revision * REV_MULT)
1422
1423 vers_stage = version / STAGE_MULT
1424 version = version - (vers_stage * STAGE_MULT)
1425
1426 vers_stage_level = version
1427
1428 out_str = "%d.%d" % (vers_major, vers_minor)
1429 if vers_revision > 0: out_str += ".%d" % vers_revision
1430 if vers_stage == 1 : out_str += "d%d" % vers_stage_level
1431 if vers_stage == 3 : out_str += "a%d" % vers_stage_level
1432 if vers_stage == 5 : out_str += "b%d" % vers_stage_level
1433 if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
1434
1435 return out_str
1436
1437 @lldb_command('showallknownkmods')
1438 def ShowAllKnownKexts(cmd_args=None):
1439 """ Display a summary listing of all kexts known in the system.
1440 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1441 """
1442 kext_count = int(kern.globals.sKextsByID.count)
1443 index = 0
1444 kext_dictionary = kern.globals.sKextsByID.dictionary
1445 print "%d kexts in sKextsByID:" % kext_count
1446 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1447 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1448
1449 while index < kext_count:
1450 kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
1451 kext_name = str(kext_dict.key.string)
1452 osk = Cast(kext_dict.value, 'OSKext *')
1453 if int(osk.flags.loaded) :
1454 load_addr = "{0: <#020x}".format(osk.kmod_info)
1455 id = "{0: >5d}".format(osk.loadTag)
1456 else:
1457 load_addr = "------"
1458 id = "--"
1459 version_num = unsigned(osk.version)
1460 version = GetOSKextVersion(version_num)
1461 print format_string.format(osk, load_addr, id, version, kext_name)
1462 index += 1
1463
1464 return
1465
1466 def FindKmodNameForAddr(addr):
1467 """ Given an address, return the name of the kext containing that address
1468 """
1469 addr = unsigned(addr)
1470 all_kexts_info = GetKextLoadInformation()
1471 for kinfo in all_kexts_info:
1472 segment = macho.get_segment_with_addr(kinfo[4], addr)
1473 if segment:
1474 return kinfo[7].name
1475 return None
1476
1477
1478 @lldb_command('addkextaddr')
1479 def AddKextAddr(cmd_args=[]):
1480 """ Given an address, load the kext which contains that address
1481 Syntax: (lldb) addkextaddr <addr>
1482 """
1483 if len(cmd_args) < 1:
1484 raise ArgumentError("Insufficient arguments")
1485
1486 addr = ArgumentStringToInt(cmd_args[0])
1487 all_kexts_info = GetKextLoadInformation()
1488 found_kinfo = None
1489 found_segment = None
1490 for kinfo in all_kexts_info:
1491 segment = macho.get_segment_with_addr(kinfo[4], addr)
1492 if segment:
1493 print GetKextSummary.header
1494 print GetKextSummary(kinfo[7]) + " segment: {} offset = {:#0x}".format(segment.name, (addr - segment.vmaddr))
1495 cur_uuid = kinfo[0].lower()
1496 print "Fetching dSYM for %s" % cur_uuid
1497 info = dsymForUUID(cur_uuid)
1498 if info and 'DBGSymbolRichExecutable' in info:
1499 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1500 addDSYM(cur_uuid, info)
1501 loadDSYM(cur_uuid, int(kinfo[1],16), kinfo[4])
1502 else:
1503 print "Failed to get symbol info for %s" % cur_uuid
1504 return
1505
1506
1507 @lldb_command('showkmodaddr')
1508 def ShowKmodAddr(cmd_args=[]):
1509 """ Given an address, print the offset and name for the kmod containing it
1510 Syntax: (lldb) showkmodaddr <addr>
1511 """
1512 if len(cmd_args) < 1:
1513 raise ArgumentError("Insufficient arguments")
1514
1515 addr = ArgumentStringToInt(cmd_args[0])
1516 all_kexts_info = GetKextLoadInformation()
1517 found_kinfo = None
1518 found_segment = None
1519 for kinfo in all_kexts_info:
1520 s = macho.get_segment_with_addr(kinfo[4], addr)
1521 if s:
1522 found_segment = s
1523 found_kinfo = kinfo
1524 break
1525 if found_kinfo:
1526 print GetKextSummary.header
1527 print GetKextSummary(found_kinfo[7]) + " segment: {} offset = {:#0x}".format(found_segment.name, (addr - found_segment.vmaddr))
1528 return True
1529 return False
1530
1531
1532 @lldb_command('addkext','AF:N:')
1533 def AddKextSyms(cmd_args=[], cmd_options={}):
1534 """ Add kext symbols into lldb.
1535 This command finds symbols for a uuid and load the required executable
1536 Usage:
1537 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1538 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1539 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
1540 addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
1541 addkext all : Will load all the kext symbols - SLOW
1542 """
1543
1544
1545 if "-F" in cmd_options:
1546 exec_path = cmd_options["-F"]
1547 exec_full_path = ResolveFSPath(exec_path)
1548 if not os.path.exists(exec_full_path):
1549 raise ArgumentError("Unable to resolve {:s}".format(exec_path))
1550
1551 if not os.path.isfile(exec_full_path):
1552 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1553 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1554 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
1555
1556 slide_value = None
1557 sections = None
1558 if cmd_args:
1559 slide_value = cmd_args[0]
1560 debuglog("loading slide value from user input %s" % cmd_args[0])
1561
1562 filespec = lldb.SBFileSpec(exec_full_path, False)
1563 print "target modules add %s" % exec_full_path
1564 print lldb_run_command("target modules add %s" % exec_full_path)
1565 loaded_module = LazyTarget.GetTarget().FindModule(filespec)
1566 if loaded_module.IsValid():
1567 uuid_str = loaded_module.GetUUIDString()
1568 debuglog("added module %s with uuid %s" % (exec_full_path, uuid_str))
1569 if slide_value is None:
1570 all_kexts_info = GetKextLoadInformation()
1571 for k in all_kexts_info:
1572 debuglog(k[0])
1573 if k[0].lower() == uuid_str.lower():
1574 slide_value = k[1]
1575 sections = k[4]
1576 debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
1577 if slide_value is None:
1578 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
1579
1580 if not sections:
1581 cmd_str = "target modules load --file %s --slide %s" % ( exec_full_path, str(slide_value))
1582 debuglog(cmd_str)
1583 else:
1584 cmd_str = "target modules load --file {} ".format(exec_full_path)
1585 sections_str = ""
1586 for s in sections:
1587 sections_str += " {} {:#0x} ".format(s.name, s.vmaddr)
1588 cmd_str += sections_str
1589 debuglog(cmd_str)
1590
1591 lldb.debugger.HandleCommand(cmd_str)
1592
1593 kern.symbolicator = None
1594 return True
1595
1596 all_kexts_info = GetKextLoadInformation()
1597
1598 if "-N" in cmd_options:
1599 kext_name = cmd_options["-N"]
1600 kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
1601 if len(kext_name_matches) != 1 and "-A" not in cmd_options:
1602 print "Ambiguous match for name: {:s}".format(kext_name)
1603 if len(kext_name_matches) > 0:
1604 print "Options are:\n\t" + "\n\t".join(kext_name_matches)
1605 return
1606 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
1607 for cur_knm in kext_name_matches:
1608 for x in all_kexts_info:
1609 if cur_knm == x[2]:
1610 cur_uuid = x[0].lower()
1611 print "Fetching dSYM for {:s}".format(cur_uuid)
1612 info = dsymForUUID(cur_uuid)
1613 if info and 'DBGSymbolRichExecutable' in info:
1614 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
1615 addDSYM(cur_uuid, info)
1616 loadDSYM(cur_uuid, int(x[1],16), x[4])
1617 else:
1618 print "Failed to get symbol info for {:s}".format(cur_uuid)
1619 break
1620 kern.symbolicator = None
1621 return
1622
1623 if len(cmd_args) < 1:
1624 raise ArgumentError("No arguments specified.")
1625
1626 uuid = cmd_args[0].lower()
1627
1628 load_all_kexts = False
1629 if uuid == "all":
1630 load_all_kexts = True
1631
1632 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
1633 raise ArgumentError("Unknown argument {:s}".format(uuid))
1634
1635 for k_info in all_kexts_info:
1636 cur_uuid = k_info[0].lower()
1637 if load_all_kexts or (uuid == cur_uuid):
1638 print "Fetching dSYM for %s" % cur_uuid
1639 info = dsymForUUID(cur_uuid)
1640 if info and 'DBGSymbolRichExecutable' in info:
1641 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1642 addDSYM(cur_uuid, info)
1643 loadDSYM(cur_uuid, int(k_info[1],16), k_info[4])
1644 else:
1645 print "Failed to get symbol info for %s" % cur_uuid
1646 #end of for loop
1647 kern.symbolicator = None
1648 return True
1649
1650
1651
1652 lldb_alias('showkmod', 'showkmodaddr')
1653 lldb_alias('showkext', 'showkmodaddr')
1654 lldb_alias('showkextaddr', 'showkmodaddr')
1655
1656 @lldb_type_summary(['mount *'])
1657 @header("{0: <20s} {1: <20s} {2: <20s} {3: <12s} {4: <12s} {5: <12s} {6: >6s} {7: <30s} {8: <35s} {9: <30s}".format('volume(mp)', 'mnt_data', 'mnt_devvp', 'flag', 'kern_flag', 'lflag', 'type', 'mnton', 'mntfrom', 'iosched supported'))
1658 def GetMountSummary(mount):
1659 """ Display a summary of mount on the system
1660 """
1661 out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1662 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
1663 "{vfs.f_mntonname: <30s} {vfs.f_mntfromname: <35s} {iomode: <30s}").format(mnt=mount, vfs=mount.mnt_vfsstat, iomode=('Yes' if (mount.mnt_ioflags & 0x4) else 'No'))
1664 return out_string
1665
1666 @lldb_command('showallmounts')
1667 def ShowAllMounts(cmd_args=None):
1668 """ Print all mount points
1669 """
1670 mntlist = kern.globals.mountlist
1671 print GetMountSummary.header
1672 for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1673 print GetMountSummary(mnt)
1674 return
1675
1676 lldb_alias('ShowAllVols', 'showallmounts')
1677
1678 @lldb_command('systemlog')
1679 def ShowSystemLog(cmd_args=None):
1680 """ Display the kernel's printf ring buffer """
1681 msgbufp = kern.globals.msgbufp
1682 msg_size = int(msgbufp.msg_size)
1683 msg_bufx = int(msgbufp.msg_bufx)
1684 msg_bufr = int(msgbufp.msg_bufr)
1685 msg_bufc = msgbufp.msg_bufc
1686 msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size)
1687
1688 # the buffer is circular; start at the write pointer to end,
1689 # then from beginning to write pointer
1690 line = ''
1691 err = lldb.SBError()
1692 for i in range(msg_bufx, msg_size) + range(0, msg_bufx) :
1693 err.Clear()
1694 cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
1695 if not err.Success() :
1696 raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString())
1697 c = chr(cbyte)
1698 if c == '\0' :
1699 continue
1700 elif c == '\n' :
1701 print line
1702 line = ''
1703 else :
1704 line += c
1705
1706 if len(line) > 0 :
1707 print line
1708
1709 return
1710
1711 @static_var('output','')
1712 def _GetVnodePathName(vnode, vnodename):
1713 """ Internal function to get vnode path string from vnode structure.
1714 params:
1715 vnode - core.value
1716 vnodename - str
1717 returns Nothing. The output will be stored in the static variable.
1718 """
1719 if not vnode:
1720 return
1721 if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0:
1722 if int(vnode.v_mount.mnt_vnodecovered):
1723 _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
1724 else:
1725 _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
1726 _GetVnodePathName.output += "/%s" % vnodename
1727
1728 def GetVnodePath(vnode):
1729 """ Get string representation of the vnode
1730 params: vnodeval - value representing vnode * in the kernel
1731 return: str - of format /path/to/something
1732 """
1733 out_str = ''
1734 if vnode:
1735 if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) :
1736 out_str += "/"
1737 else:
1738 _GetVnodePathName.output = ''
1739 if abs(vnode.v_name) != 0:
1740 _GetVnodePathName(vnode, str(vnode.v_name))
1741 out_str += _GetVnodePathName.output
1742 else:
1743 out_str += 'v_name = NULL'
1744 _GetVnodePathName.output = ''
1745 return out_str
1746
1747
1748 @lldb_command('showvnodepath')
1749 def ShowVnodePath(cmd_args=None):
1750 """ Prints the path for a vnode
1751 usage: showvnodepath <vnode>
1752 """
1753 if cmd_args != None and len(cmd_args) > 0 :
1754 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1755 if vnode_val:
1756 print GetVnodePath(vnode_val)
1757 return
1758
1759 # Macro: showvnodedev
1760 def GetVnodeDevInfo(vnode):
1761 """ Internal function to get information from the device type vnodes
1762 params: vnode - value representing struct vnode *
1763 return: str - formatted output information for block and char vnode types passed as param
1764 """
1765 vnodedev_output = ""
1766 vblk_type = GetEnumValue('vtype::VBLK')
1767 vchr_type = GetEnumValue('vtype::VCHR')
1768 if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type):
1769 devnode = Cast(vnode.v_data, 'devnode_t *')
1770 devnode_dev = devnode.dn_typeinfo.dev
1771 devnode_major = (devnode_dev >> 24) & 0xff
1772 devnode_minor = devnode_dev & 0x00ffffff
1773
1774 # boilerplate device information for a vnode
1775 vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
1776 vnodedev_output += "\n\t type:\t\t"
1777 if (vnode.v_type == vblk_type):
1778 vnodedev_output += "VBLK"
1779 if (vnode.v_type == vchr_type):
1780 vnodedev_output += "VCHR"
1781 vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name)
1782 vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor)
1783 vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode))
1784 vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid)
1785
1786 # decode device specific data
1787 vnodedev_output += "\nDevice Specific Information:\t"
1788 if (vnode.v_type == vblk_type):
1789 vnodedev_output += "Sorry, I do not know how to decode block devices yet!"
1790 vnodedev_output += "\nMaybe you can write me!"
1791
1792 if (vnode.v_type == vchr_type):
1793 # Device information; this is scanty
1794 # range check
1795 if (devnode_major > 42) or (devnode_major < 0):
1796 vnodedev_output += "Invalid major #\n"
1797 # static assignments in conf
1798 elif (devnode_major == 0):
1799 vnodedev_output += "Console mux device\n"
1800 elif (devnode_major == 2):
1801 vnodedev_output += "Current tty alias\n"
1802 elif (devnode_major == 3):
1803 vnodedev_output += "NULL device\n"
1804 elif (devnode_major == 4):
1805 vnodedev_output += "Old pty slave\n"
1806 elif (devnode_major == 5):
1807 vnodedev_output += "Old pty master\n"
1808 elif (devnode_major == 6):
1809 vnodedev_output += "Kernel log\n"
1810 elif (devnode_major == 12):
1811 vnodedev_output += "Memory devices\n"
1812 # Statically linked dynamic assignments
1813 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')):
1814 vnodedev_output += "Cloning pty master not done\n"
1815 #GetVnodeDevCpty(devnode_major, devnode_minor)
1816 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')):
1817 vnodedev_output += "Cloning pty slave not done\n"
1818 #GetVnodeDevCpty(devnode_major, devnode_minor)
1819 else:
1820 vnodedev_output += "RESERVED SLOT\n"
1821 else:
1822 vnodedev_output += "{:#x} is not a device".format(vnode)
1823 return vnodedev_output
1824
1825 @lldb_command('showvnodedev')
1826 def ShowVnodeDev(cmd_args=None):
1827 """ Routine to display details of all vnodes of block and character device types
1828 Usage: showvnodedev <address of vnode>
1829 """
1830 if not cmd_args:
1831 print "No arguments passed"
1832 print ShowVnodeDev.__doc__
1833 return False
1834 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1835 if not vnode_val:
1836 print "unknown arguments:", str(cmd_args)
1837 return False
1838 print GetVnodeDevInfo(vnode_val)
1839
1840 # EndMacro: showvnodedev
1841
1842 # Macro: showvnodelocks
1843 def GetVnodeLock(lockf):
1844 """ Internal function to get information from the given advisory lock
1845 params: lockf - value representing v_lockf member in struct vnode *
1846 return: str - formatted output information for the advisory lock
1847 """
1848 vnode_lock_output = ''
1849 lockf_flags = lockf.lf_flags
1850 lockf_type = lockf.lf_type
1851 if lockf_flags & 0x20:
1852 vnode_lock_output += ("{: <8s}").format('flock')
1853 if lockf_flags & 0x40:
1854 vnode_lock_output += ("{: <8s}").format('posix')
1855 if lockf_flags & 0x80:
1856 vnode_lock_output += ("{: <8s}").format('prov')
1857 if lockf_flags & 0x10:
1858 vnode_lock_output += ("{: <4s}").format('W')
1859 if lockf_flags & 0x400:
1860 vnode_lock_output += ("{: <8s}").format('ofd')
1861 else:
1862 vnode_lock_output += ("{: <4s}").format('.')
1863
1864 # POSIX file vs advisory range locks
1865 if lockf_flags & 0x40:
1866 lockf_proc = Cast(lockf.lf_id, 'proc *')
1867 vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
1868 else:
1869 vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
1870
1871 # lock type
1872 if lockf_type == 1:
1873 vnode_lock_output += ("{: <12s}").format('shared')
1874 else:
1875 if lockf_type == 3:
1876 vnode_lock_output += ("{: <12s}").format('exclusive')
1877 else:
1878 if lockf_type == 2:
1879 vnode_lock_output += ("{: <12s}").format('unlock')
1880 else:
1881 vnode_lock_output += ("{: <12s}").format('unknown')
1882
1883 # start and stop values
1884 vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
1885 vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
1886 return vnode_lock_output
1887
1888 @header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
1889 def GetVnodeLocksSummary(vnode):
1890 """ Internal function to get summary of advisory locks for the given vnode
1891 params: vnode - value representing the vnode object
1892 return: str - formatted output information for the summary of advisory locks
1893 """
1894 out_str = ''
1895 if vnode:
1896 lockf_list = vnode.v_lockf
1897 for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'):
1898 out_str += ("{: <4s}").format('H')
1899 out_str += GetVnodeLock(lockf_itr)
1900 lockf_blocker = lockf_itr.lf_blkhd.tqh_first
1901 while lockf_blocker:
1902 out_str += ("{: <4s}").format('>')
1903 out_str += GetVnodeLock(lockf_blocker)
1904 lockf_blocker = lockf_blocker.lf_block.tqe_next
1905 return out_str
1906
1907 @lldb_command('showvnodelocks')
1908 def ShowVnodeLocks(cmd_args=None):
1909 """ Routine to display list of advisory record locks for the given vnode address
1910 Usage: showvnodelocks <address of vnode>
1911 """
1912 if not cmd_args:
1913 print "No arguments passed"
1914 print ShowVnodeLocks.__doc__
1915 return False
1916 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1917 if not vnode_val:
1918 print "unknown arguments:", str(cmd_args)
1919 return False
1920 print GetVnodeLocksSummary.header
1921 print GetVnodeLocksSummary(vnode_val)
1922
1923 # EndMacro: showvnodelocks
1924
1925 # Macro: showproclocks
1926
1927 @lldb_command('showproclocks')
1928 def ShowProcLocks(cmd_args=None):
1929 """ Routine to display list of advisory record locks for the given process
1930 Usage: showproclocks <address of proc>
1931 """
1932 if not cmd_args:
1933 print "No arguments passed"
1934 print ShowProcLocks.__doc__
1935 return False
1936 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *')
1937 if not proc:
1938 print "unknown arguments:", str(cmd_args)
1939 return False
1940 out_str = ''
1941 proc_filedesc = proc.p_fd
1942 fd_lastfile = proc_filedesc.fd_lastfile
1943 fd_ofiles = proc_filedesc.fd_ofiles
1944 count = 0
1945 seen = 0
1946 while count <= fd_lastfile:
1947 if fd_ofiles[count]:
1948 fglob = fd_ofiles[count].f_fglob
1949 fo_type = fglob.fg_ops.fo_type
1950 if fo_type == 1:
1951 fg_data = fglob.fg_data
1952 fg_vnode = Cast(fg_data, 'vnode *')
1953 name = fg_vnode.v_name
1954 lockf_itr = fg_vnode.v_lockf
1955 if lockf_itr:
1956 if not seen:
1957 print GetVnodeLocksSummary.header
1958 seen = seen + 1
1959 out_str += ("\n( fd {:d}, name ").format(count)
1960 if not name:
1961 out_str += "(null) )\n"
1962 else:
1963 out_str += "{:s} )\n".format(name)
1964 print out_str
1965 print GetVnodeLocksSummary(fg_vnode)
1966 count = count + 1
1967 print "\n{0: d} total locks for {1: #018x}".format(seen, proc)
1968
1969 # EndMacro: showproclocks
1970
1971 @lldb_type_summary(['vnode_t', 'vnode *'])
1972 @header("{0: <20s} {1: >8s} {2: >8s} {3: <20s} {4: <6s} {5: <20s} {6: <6s} {7: <6s} {8: <35s}".format('vnode', 'usecount', 'iocount', 'v_data', 'vtype', 'parent', 'mapped', 'cs_version', 'name'))
1973 def GetVnodeSummary(vnode):
1974 """ Get a summary of important information out of vnode
1975 """
1976 out_str = ''
1977 format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <6s} {8: <35s}"
1978 usecount = int(vnode.v_usecount)
1979 iocount = int(vnode.v_iocount)
1980 v_data_ptr = int(hex(vnode.v_data), 16)
1981 vtype = int(vnode.v_type)
1982 vtype_str = "%d" % vtype
1983 vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
1984 if vtype >= 0 and vtype < len(vnode_types):
1985 vtype_str = vnode_types[vtype]
1986 parent_ptr = int(hex(vnode.v_parent), 16)
1987 name_ptr = int(hex(vnode.v_name), 16)
1988 name =""
1989 if name_ptr != 0:
1990 name = str(vnode.v_name)
1991 elif int(vnode.v_tag) == 16 :
1992 cnode = Cast(vnode.v_data, 'cnode *')
1993 name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
1994 mapped = '-'
1995 csblob_version = '-'
1996 if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
1997 csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen)
1998 # Check to see if vnode is mapped/unmapped
1999 if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
2000 mapped = '1'
2001 else:
2002 mapped = '0'
2003 out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name)
2004 return out_str
2005
2006 @lldb_command('showallvnodes')
2007 def ShowAllVnodes(cmd_args=None):
2008 """ Display info about all vnodes
2009 """
2010 mntlist = kern.globals.mountlist
2011 print GetVnodeSummary.header
2012 for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
2013 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2014 print GetVnodeSummary(vnodeval)
2015 return
2016
2017 @lldb_command('showvnode')
2018 def ShowVnode(cmd_args=None):
2019 """ Display info about one vnode
2020 usage: showvnode <vnode>
2021 """
2022 if cmd_args == None or len(cmd_args) < 1:
2023 print "Please provide valid vnode argument. Type help showvnode for help."
2024 return
2025 vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
2026 print GetVnodeSummary.header
2027 print GetVnodeSummary(vnodeval)
2028
2029 @lldb_command('showvolvnodes')
2030 def ShowVolVnodes(cmd_args=None):
2031 """ Display info about all vnodes of a given mount_t
2032 """
2033 if cmd_args == None or len(cmd_args) < 1:
2034 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
2035 return
2036 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2037 print GetVnodeSummary.header
2038 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2039 print GetVnodeSummary(vnodeval)
2040 return
2041
2042 @lldb_command('showvolbusyvnodes')
2043 def ShowVolBusyVnodes(cmd_args=None):
2044 """ Display info about busy (iocount!=0) vnodes of a given mount_t
2045 """
2046 if cmd_args == None or len(cmd_args) < 1:
2047 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
2048 return
2049 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
2050 print GetVnodeSummary.header
2051 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
2052 if int(vnodeval.v_iocount) != 0:
2053 print GetVnodeSummary(vnodeval)
2054
2055 @lldb_command('showallbusyvnodes')
2056 def ShowAllBusyVnodes(cmd_args=None):
2057 """ Display info about all busy (iocount!=0) vnodes
2058 """
2059 mntlistval = kern.globals.mountlist
2060 for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'):
2061 ShowVolBusyVnodes([hex(mntval)])
2062
2063 @lldb_command('print_vnode')
2064 def PrintVnode(cmd_args=None):
2065 """ Prints out the fields of a vnode struct
2066 Usage: print_vnode <vnode>
2067 """
2068 if not cmd_args:
2069 print "Please provide valid vnode argument. Type help print_vnode for help."
2070 return
2071 ShowVnode(cmd_args)
2072
2073 @lldb_command('showworkqvnodes')
2074 def ShowWorkqVnodes(cmd_args=None):
2075 """ Print the vnode worker list
2076 Usage: showworkqvnodes <struct mount *>
2077 """
2078 if not cmd_args:
2079 print "Please provide valid mount argument. Type help showworkqvnodes for help."
2080 return
2081
2082 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2083 vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *')
2084 print GetVnodeSummary.header
2085 while int(vp) != 0:
2086 print GetVnodeSummary(vp)
2087 vp = vp.v_mntvnodes.tqe_next
2088
2089 @lldb_command('shownewvnodes')
2090 def ShowNewVnodes(cmd_args=None):
2091 """ Print the new vnode list
2092 Usage: shownewvnodes <struct mount *>
2093 """
2094 if not cmd_args:
2095 print "Please provide valid mount argument. Type help shownewvnodes for help."
2096 return
2097 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
2098 vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *')
2099 print GetVnodeSummary.header
2100 while int(vp) != 0:
2101 print GetVnodeSummary(vp)
2102 vp = vp.v_mntvnodes.tqe_next
2103
2104
2105 @lldb_command('showprocvnodes')
2106 def ShowProcVnodes(cmd_args=None):
2107 """ Routine to print out all the open fds which are vnodes in a process
2108 Usage: showprocvnodes <proc *>
2109 """
2110 if not cmd_args:
2111 print "Please provide valid proc argument. Type help showprocvnodes for help."
2112 return
2113 procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *')
2114 fdptr = Cast(procptr.p_fd, 'filedesc *')
2115 if int(fdptr.fd_cdir) != 0:
2116 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir))
2117 if int(fdptr.fd_rdir) != 0:
2118 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
2119 count = 0
2120 print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary.header
2121 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
2122 fpptr = Cast(fdptr.fd_ofiles, 'fileproc *')
2123 while count < fdptr.fd_nfiles:
2124 fpp = dereference(fpptr)
2125 fproc = Cast(fpp, 'fileproc *')
2126 if int(fproc) != 0:
2127 fglob = dereference(fproc).f_fglob
2128 flags = ""
2129 if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
2130 if (fdptr.fd_ofileflags[count] & 1): flags += 'E'
2131 if (fdptr.fd_ofileflags[count] & 2): flags += 'F'
2132 if (fdptr.fd_ofileflags[count] & 4): flags += 'R'
2133 if (fdptr.fd_ofileflags[count] & 8): flags += 'C'
2134 print '{0: <5d} {1: <7s}'.format(count, flags) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
2135 count += 1
2136 fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'fileproc *')
2137
2138 @lldb_command('showallprocvnodes')
2139 def ShowAllProcVnodes(cmd_args=None):
2140 """ Routine to print out all the open fds which are vnodes
2141 """
2142
2143 procptr = Cast(kern.globals.allproc.lh_first, 'proc *')
2144 while procptr and int(procptr) != 0:
2145 print '{:<s}'.format("=" * 106)
2146 print GetProcInfo(procptr)
2147 ShowProcVnodes([int(procptr)])
2148 procptr = procptr.p_list.le_next
2149
2150 @xnudebug_test('test_vnode')
2151 def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
2152 """ Test the functionality of vnode related commands
2153 returns
2154 - False on failure
2155 - True on success
2156 """
2157 if not isConnected:
2158 print "Target is not connected. Cannot test memstats"
2159 return False
2160 res = lldb.SBCommandReturnObject()
2161 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res)
2162 result = res.GetOutput()
2163 if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
2164 return True
2165 else:
2166 return False
2167
2168 # Macro: showallmtx
2169 @lldb_type_summary(['_lck_grp_ *'])
2170 def GetMutexEntry(mtxg):
2171 """ Summarize a mutex group entry with important information.
2172 params:
2173 mtxg: value - obj representing a mutex group in kernel
2174 returns:
2175 out_string - summary of the mutex group
2176 """
2177 out_string = ""
2178
2179 if kern.ptrsize == 8:
2180 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2181 else:
2182 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2183
2184 if mtxg.lck_grp_mtxcnt:
2185 out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt,
2186 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt,
2187 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name)
2188 return out_string
2189
2190 @lldb_command('showallmtx')
2191 def ShowAllMtx(cmd_args=None):
2192 """ Routine to print a summary listing of all mutexes
2193 """
2194
2195 if kern.ptrsize == 8:
2196 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2197 else:
2198 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2199
2200 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2201
2202 mtxgrp_queue_head = kern.globals.lck_grp_queue
2203 mtxgrp_ptr_type = GetType('_lck_grp_ *')
2204
2205 for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
2206 print GetMutexEntry(mtxgrp_ptr)
2207 return
2208 # EndMacro: showallmtx
2209
2210 # Macro: showallrwlck
2211 @lldb_type_summary(['_lck_grp_ *'])
2212 def GetRWLEntry(rwlg):
2213 """ Summarize a reader writer lock group with important information.
2214 params:
2215 rwlg: value - obj representing a reader writer lock group in kernel
2216 returns:
2217 out_string - summary of the reader writer lock group
2218 """
2219 out_string = ""
2220
2221 if kern.ptrsize == 8:
2222 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2223 else:
2224 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
2225
2226 if rwlg.lck_grp_rwcnt:
2227 out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt,
2228 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt,
2229 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
2230 return out_string
2231
2232 #Macro: showlock
2233 @lldb_type_summary(['lck_mtx_t *'])
2234 @header("===== Mutex Lock Summary =====")
2235 def GetMutexLockSummary(mtx):
2236 """ Summarize mutex lock with important information.
2237 params:
2238 mtx: value - obj representing a mutex lock in kernel
2239 returns:
2240 out_str - summary of the mutex lock
2241 """
2242 if not mtx:
2243 return "Invalid lock value: 0x0"
2244
2245 if kern.arch == "x86_64":
2246 out_str = "Lock Type : MUTEX\n"
2247 if mtx.lck_mtx_tag == 0x07ff1007 :
2248 out_str += "Tagged as indirect, printing ext lock at: {:#x}\n".format(mtx.lck_mtx_ptr)
2249 mtx = Cast(mtx.lck_mtx_ptr, 'lck_mtx_t *')
2250
2251 if mtx.lck_mtx_tag == 0x07fe2007 :
2252 out_str += "*** Tagged as DESTROYED ({:#x}) ***\n".format(mtx.lck_mtx_tag)
2253
2254 out_str += "Owner Thread : {mtx.lck_mtx_owner:#x}\n".format(mtx=mtx)
2255 out_str += "Number of Waiters : {mtx.lck_mtx_waiters:#x}\n".format(mtx=mtx)
2256 out_str += "ILocked : {mtx.lck_mtx_ilocked:#x}\n".format(mtx=mtx)
2257 out_str += "MLocked : {mtx.lck_mtx_mlocked:#x}\n".format(mtx=mtx)
2258 out_str += "Promoted : {mtx.lck_mtx_promoted:#x}\n".format(mtx=mtx)
2259 out_str += "Pri : {mtx.lck_mtx_pri:#x}\n".format(mtx=mtx)
2260 out_str += "Spin : {mtx.lck_mtx_spin:#x}\n".format(mtx=mtx)
2261 out_str += "Ext : {mtx.lck_mtx_is_ext:#x}\n".format(mtx=mtx)
2262 if mtx.lck_mtxd_pad32 == 0xFFFFFFFF :
2263 out_str += "Canary (valid) : {mtx.lck_mtxd_pad32:#x}\n".format(mtx=mtx)
2264 else:
2265 out_str += "Canary (INVALID) : {mtx.lck_mtxd_pad32:#x}\n".format(mtx=mtx)
2266 return out_str
2267
2268 out_str = "Lock Type\t\t: MUTEX\n"
2269 out_str += "Owner Thread\t\t: {:#x}".format(mtx.lck_mtx_data & ~0x3)
2270 if (mtx.lck_mtx_data & ~0x3) == 0xfffffff0:
2271 out_str += " Held as spinlock"
2272 out_str += "\nNumber of Waiters\t: {:d}\n".format(mtx.lck_mtx_waiters)
2273 out_str += "Flags\t\t\t: "
2274 if mtx.lck_mtx_data & 0x1:
2275 out_str += "[Interlock Locked] "
2276 if mtx.lck_mtx_data & 0x2:
2277 out_str += "[Wait Flag]"
2278 return out_str
2279
2280 @lldb_type_summary(['lck_spin_t *'])
2281 @header("===== SpinLock Summary =====")
2282 def GetSpinLockSummary(spinlock):
2283 """ Summarize spinlock with important information.
2284 params:
2285 spinlock: value - obj representing a spinlock in kernel
2286 returns:
2287 out_str - summary of the spinlock
2288 """
2289 if not spinlock:
2290 return "Invalid lock value: 0x0"
2291
2292 out_str = "Lock Type\t\t: SPINLOCK\n"
2293 if kern.arch == "x86_64":
2294 out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
2295 return out_str
2296
2297 lock_data = spinlock.hwlock.lock_data
2298 if lock_data == 1:
2299 out_str += "Invalid state: interlock is locked but no owner\n"
2300 return out_str
2301 out_str += "Owner Thread\t\t: "
2302 if lock_data == 0:
2303 out_str += "None\n"
2304 else:
2305 out_str += "{:#x}\n".format(lock_data & ~0x1)
2306 if (lock_data & 1) == 0:
2307 out_str += "Invalid state: owned but interlock bit is not set\n"
2308 return out_str
2309
2310 @lldb_command('showlock', 'MS')
2311 def ShowLock(cmd_args=None, cmd_options={}):
2312 """ Show info about a lock - its state and owner thread details
2313 Usage: showlock <address of a lock>
2314 -M : to consider <addr> as lck_mtx_t
2315 -S : to consider <addr> as lck_spin_t
2316 """
2317 if not cmd_args:
2318 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
2319 return
2320
2321 summary_str = ""
2322 lock = kern.GetValueFromAddress(cmd_args[0], 'uintptr_t *')
2323
2324 if kern.arch == "x86_64" and lock:
2325 if "-M" in cmd_options:
2326 lock_mtx = kern.GetValueFromAddress(lock, 'lck_mtx_t *')
2327 summary_str = GetMutexLockSummary(lock_mtx)
2328 elif "-S" in cmd_options:
2329 lock_spin = kern.GetValueFromAddress(lock, 'lck_spin_t *')
2330 summary_str = GetSpinLockSummary(lock_spin)
2331 else:
2332 summary_str = "Please specify supported lock option(-M/-S)"
2333
2334 print summary_str
2335 return
2336
2337 if lock:
2338 lock_mtx = Cast(lock, 'lck_mtx_t*')
2339 if lock_mtx.lck_mtx_type == 0x22:
2340 summary_str = GetMutexLockSummary(lock_mtx)
2341
2342 lock_spin = Cast(lock, 'lck_spin_t*')
2343 if lock_spin.type == 0x11:
2344 summary_str = GetSpinLockSummary(lock_spin)
2345
2346 if summary_str == "":
2347 summary_str = "Lock Type\t\t: INVALID LOCK"
2348 print summary_str
2349
2350 #EndMacro: showlock
2351
2352 @lldb_command('showallrwlck')
2353 def ShowAllRWLck(cmd_args=None):
2354 """ Routine to print a summary listing of all read/writer locks
2355 """
2356 if kern.ptrsize == 8:
2357 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2358 else:
2359 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2360
2361 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2362
2363 rwlgrp_queue_head = kern.globals.lck_grp_queue
2364 rwlgrp_ptr_type = GetType('_lck_grp_ *')
2365 for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"):
2366 print GetRWLEntry(rwlgrp_ptr)
2367 return
2368 # EndMacro: showallrwlck
2369
2370 #Macro: showbootermemorymap
2371 @lldb_command('showbootermemorymap')
2372 def ShowBooterMemoryMap(cmd_args=None):
2373 """ Prints out the phys memory map from kernelBootArgs
2374 Supported only on x86_64
2375 """
2376 if kern.arch != 'x86_64':
2377 print "showbootermemorymap not supported on this architecture"
2378 return
2379
2380 out_string = ""
2381
2382 # Memory type map
2383 memtype_dict = {
2384 0: 'Reserved',
2385 1: 'LoaderCode',
2386 2: 'LoaderData',
2387 3: 'BS_code',
2388 4: 'BS_data',
2389 5: 'RT_code',
2390 6: 'RT_data',
2391 7: 'Convention',
2392 8: 'Unusable',
2393 9: 'ACPI_recl',
2394 10: 'ACPI_NVS',
2395 11: 'MemMapIO',
2396 12: 'MemPortIO',
2397 13: 'PAL_code'
2398 }
2399
2400 boot_args = kern.globals.kernelBootArgs
2401 msize = boot_args.MemoryMapDescriptorSize
2402 mcount = (boot_args.MemoryMapSize) / unsigned(msize)
2403
2404 out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2405
2406 i = 0
2407 while i < mcount:
2408 mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + kern.VM_MIN_KERNEL_ADDRESS + unsigned(i*msize), 'EfiMemoryRange *')
2409 mtype = unsigned(mptr.Type)
2410 if mtype in memtype_dict:
2411 out_string += "{0: <12s}".format(memtype_dict[mtype])
2412 else:
2413 out_string += "{0: <12s}".format("UNKNOWN")
2414
2415 if mptr.VirtualStart == 0:
2416 out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute)
2417 else:
2418 out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute)
2419 i = i + 1
2420
2421 print out_string
2422 #EndMacro: showbootermemorymap
2423
2424 @lldb_command('show_all_purgeable_objects')
2425 def ShowAllPurgeableVmObjects(cmd_args=None):
2426 """ Routine to print a summary listing of all the purgeable vm objects
2427 """
2428 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2429 ShowAllPurgeableVolatileVmObjects()
2430 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2431 ShowAllPurgeableNonVolatileVmObjects()
2432
2433 @lldb_command('show_all_purgeable_nonvolatile_objects')
2434 def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
2435 """ Routine to print a summary listing of all the vm objects in
2436 the purgeable_nonvolatile_queue
2437 """
2438
2439 nonvolatile_total = lambda:None
2440 nonvolatile_total.objects = 0
2441 nonvolatile_total.vsize = 0
2442 nonvolatile_total.rsize = 0
2443 nonvolatile_total.wsize = 0
2444 nonvolatile_total.csize = 0
2445 nonvolatile_total.disowned_objects = 0
2446 nonvolatile_total.disowned_vsize = 0
2447 nonvolatile_total.disowned_rsize = 0
2448 nonvolatile_total.disowned_wsize = 0
2449 nonvolatile_total.disowned_csize = 0
2450
2451 queue_len = kern.globals.purgeable_nonvolatile_count
2452 queue_head = kern.globals.purgeable_nonvolatile_queue
2453
2454 print 'purgeable_nonvolatile_queue:{:#018x} purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
2455 print 'N:non-volatile V:volatile E:empty D:deny\n'
2456
2457 print '{:>6s} {:<6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s}\n'.format("#","#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process")
2458 idx = 0
2459 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
2460 idx += 1
2461 ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
2462 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.disowned_objects, nonvolatile_total.disowned_vsize, nonvolatile_total.disowned_rsize, nonvolatile_total.disowned_wsize, nonvolatile_total.disowned_csize)
2463 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(nonvolatile_total.objects, nonvolatile_total.vsize, nonvolatile_total.rsize, nonvolatile_total.wsize, nonvolatile_total.csize)
2464
2465
2466 def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
2467 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
2468 params:
2469 object - core.value : a object of type 'struct vm_object *'
2470 returns:
2471 None
2472 """
2473 page_size = kern.globals.page_size
2474 if object.purgable == 0:
2475 purgable = "N"
2476 elif object.purgable == 1:
2477 purgable = "V"
2478 elif object.purgable == 2:
2479 purgable = "E"
2480 elif object.purgable == 3:
2481 purgable = "D"
2482 else:
2483 purgable = "?"
2484 if object.pager == 0:
2485 compressed_count = 0
2486 else:
2487 compressor_pager = Cast(object.pager, 'compressor_pager *')
2488 compressed_count = compressor_pager.cpgr_num_slots_occupied
2489
2490 print "{:>6d}/{:<6d} {:#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:#018x} {:>6d} {:<20s}\n".format(idx,queue_len,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_un2.vou_purgeable_owner,GetProcPIDForTask(object.vo_un2.vou_purgeable_owner),GetProcNameForTask(object.vo_un2.vou_purgeable_owner))
2491
2492 nonvolatile_total.objects += 1
2493 nonvolatile_total.vsize += object.vo_un1.vou_size/page_size
2494 nonvolatile_total.rsize += object.resident_page_count
2495 nonvolatile_total.wsize += object.wired_page_count
2496 nonvolatile_total.csize += compressed_count
2497 if object.vo_un2.vou_purgeable_owner == 0:
2498 nonvolatile_total.disowned_objects += 1
2499 nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
2500 nonvolatile_total.disowned_rsize += object.resident_page_count
2501 nonvolatile_total.disowned_wsize += object.wired_page_count
2502 nonvolatile_total.disowned_csize += compressed_count
2503
2504
2505 @lldb_command('show_all_purgeable_volatile_objects')
2506 def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
2507 """ Routine to print a summary listing of all the vm objects in
2508 the purgeable queues
2509 """
2510 volatile_total = lambda:None
2511 volatile_total.objects = 0
2512 volatile_total.vsize = 0
2513 volatile_total.rsize = 0
2514 volatile_total.wsize = 0
2515 volatile_total.csize = 0
2516 volatile_total.disowned_objects = 0
2517 volatile_total.disowned_vsize = 0
2518 volatile_total.disowned_rsize = 0
2519 volatile_total.disowned_wsize = 0
2520 volatile_total.disowned_csize = 0
2521
2522 purgeable_queues = kern.globals.purgeable_queues
2523 print "---------- OBSOLETE\n"
2524 ShowPurgeableQueue(purgeable_queues[0], volatile_total)
2525 print "\n\n---------- FIFO\n"
2526 ShowPurgeableQueue(purgeable_queues[1], volatile_total)
2527 print "\n\n---------- LIFO\n"
2528 ShowPurgeableQueue(purgeable_queues[2], volatile_total)
2529
2530 print "disowned objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.disowned_objects, volatile_total.disowned_vsize, volatile_total.disowned_rsize, volatile_total.disowned_wsize, volatile_total.disowned_csize)
2531 print " all objects:{:<10d} [ virtual:{:<10d} resident:{:<10d} wired:{:<10d} compressed:{:<10d} ]\n".format(volatile_total.objects, volatile_total.vsize, volatile_total.rsize, volatile_total.wsize, volatile_total.csize)
2532 purgeable_count = kern.globals.vm_page_purgeable_count
2533 purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
2534 if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
2535 mismatch = "<--------- MISMATCH\n"
2536 else:
2537 mismatch = ""
2538 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
2539
2540
2541 def ShowPurgeableQueue(qhead, volatile_total):
2542 print "----- GROUP 0\n"
2543 ShowPurgeableGroup(qhead.objq[0], volatile_total)
2544 print "----- GROUP 1\n"
2545 ShowPurgeableGroup(qhead.objq[1], volatile_total)
2546 print "----- GROUP 2\n"
2547 ShowPurgeableGroup(qhead.objq[2], volatile_total)
2548 print "----- GROUP 3\n"
2549 ShowPurgeableGroup(qhead.objq[3], volatile_total)
2550 print "----- GROUP 4\n"
2551 ShowPurgeableGroup(qhead.objq[4], volatile_total)
2552 print "----- GROUP 5\n"
2553 ShowPurgeableGroup(qhead.objq[5], volatile_total)
2554 print "----- GROUP 6\n"
2555 ShowPurgeableGroup(qhead.objq[6], volatile_total)
2556 print "----- GROUP 7\n"
2557 ShowPurgeableGroup(qhead.objq[7], volatile_total)
2558
2559 def ShowPurgeableGroup(qhead, volatile_total):
2560 idx = 0
2561 for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
2562 if idx == 0:
2563 # print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s} {:18s} {:>6s} {:<20s} {:s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process","volatilizer","pid","process","")
2564 print "{:>6s} {:18s} {:1s} {:>6s} {:>16s} {:>10s} {:>10s} {:>10s} {:18s} {:>6s} {:<20s}\n".format("#","object","P","refcnt","size (pages)","resid","wired","compressed","owner","pid","process")
2565 idx += 1
2566 ShowPurgeableVolatileVmObject(object, idx, volatile_total)
2567
2568 def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
2569 """ Routine to print out a summary a VM object in a purgeable queue
2570 params:
2571 object - core.value : a object of type 'struct vm_object *'
2572 returns:
2573 None
2574 """
2575 ## if int(object.vo_un2.vou_purgeable_owner) != int(object.vo_purgeable_volatilizer):
2576 # diff=" !="
2577 ## else:
2578 # diff=" "
2579 page_size = kern.globals.page_size
2580 if object.purgable == 0:
2581 purgable = "N"
2582 elif object.purgable == 1:
2583 purgable = "V"
2584 elif object.purgable == 2:
2585 purgable = "E"
2586 elif object.purgable == 3:
2587 purgable = "D"
2588 else:
2589 purgable = "?"
2590 if object.pager == 0:
2591 compressed_count = 0
2592 else:
2593 compressor_pager = Cast(object.pager, 'compressor_pager *')
2594 compressed_count = compressor_pager.cpgr_num_slots_occupied
2595 # print "{:>6d} {:#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:#018x} {:>6d} {:<20s} {:#018x} {:>6d} {:<20s} {:s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count,object.vo_un2.vou_purgeable_owner,GetProcPIDForTask(object.vo_un2.vou_purgeable_owner),GetProcNameForTask(object.vo_un2.vou_purgeable_owner),object.vo_purgeable_volatilizer,GetProcPIDForTask(object.vo_purgeable_volatilizer),GetProcNameForTask(object.vo_purgeable_volatilizer),diff)
2596 print "{:>6d} {:#018x} {:1s} {:>6d} {:>16d} {:>10d} {:>10d} {:>10d} {:#018x} {:>6d} {:<20s}\n".format(idx,object,purgable,object.ref_count,object.vo_un1.vou_size/page_size,object.resident_page_count,object.wired_page_count,compressed_count, object.vo_un2.vou_purgeable_owner,GetProcPIDForTask(object.vo_un2.vou_purgeable_owner),GetProcNameForTask(object.vo_un2.vou_purgeable_owner))
2597 volatile_total.objects += 1
2598 volatile_total.vsize += object.vo_un1.vou_size/page_size
2599 volatile_total.rsize += object.resident_page_count
2600 volatile_total.wsize += object.wired_page_count
2601 volatile_total.csize += compressed_count
2602 if object.vo_un2.vou_purgeable_owner == 0:
2603 volatile_total.disowned_objects += 1
2604 volatile_total.disowned_vsize += object.vo_un1.vou_size/page_size
2605 volatile_total.disowned_rsize += object.resident_page_count
2606 volatile_total.disowned_wsize += object.wired_page_count
2607 volatile_total.disowned_csize += compressed_count
2608
2609
2610 def GetCompressedPagesForObject(obj):
2611 """Stuff
2612 """
2613 pager = Cast(obj.pager, 'compressor_pager_t')
2614 return pager.cpgr_num_slots_occupied
2615 """ # commented code below
2616 if pager.cpgr_num_slots > 128:
2617 slots_arr = pager.cpgr_slots.cpgr_islots
2618 num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
2619 index = 0
2620 compressor_slot = 0
2621 compressed_pages = 0
2622 while index < num_indirect_slot_ptr:
2623 compressor_slot = 0
2624 if slots_arr[index]:
2625 while compressor_slot < 128:
2626 if slots_arr[index][compressor_slot]:
2627 compressed_pages += 1
2628 compressor_slot += 1
2629 index += 1
2630 else:
2631 slots_arr = pager.cpgr_slots.cpgr_dslots
2632 compressor_slot = 0
2633 compressed_pages = 0
2634 while compressor_slot < pager.cpgr_num_slots:
2635 if slots_arr[compressor_slot]:
2636 compressed_pages += 1
2637 compressor_slot += 1
2638 return compressed_pages
2639 """
2640
2641 def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
2642 """ Routine to print out a summary listing of all the entries in a vm_map
2643 params:
2644 task - core.value : a object of type 'task *'
2645 returns:
2646 None
2647 """
2648 print "vm_map entries for task " + hex(task)
2649 print GetTaskSummary.header
2650 print GetTaskSummary(task)
2651 if not task.map:
2652 print "Task {0: <#020x} has map = 0x0"
2653 return None
2654 showmapvme(task.map, show_pager_info, show_all_shadows)
2655
2656 @lldb_command("showmapvme", "PS")
2657 def ShowMapVME(cmd_args=None, cmd_options={}):
2658 """Routine to print out info about the specified vm_map and its vm entries
2659 usage: showmapvme <vm_map>
2660 Use -S flag to show VM object shadow chains
2661 Use -P flag to show pager info (mapped file, compressed pages, ...)
2662 """
2663 if cmd_args == None or len(cmd_args) < 1:
2664 print "Invalid argument.", ShowMap.__doc__
2665 return
2666 show_pager_info = False
2667 show_all_shadows = False
2668 if "-P" in cmd_options:
2669 show_pager_info = True
2670 if "-S" in cmd_options:
2671 show_all_shadows = True
2672 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
2673 showmapvme(map, show_pager_info, show_all_shadows)
2674
2675 def showmapvme(map, show_pager_info, show_all_shadows):
2676 page_size = kern.globals.page_size
2677 vnode_pager_ops = kern.globals.vnode_pager_ops
2678 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
2679 rsize = 0
2680 if map.pmap != 0:
2681 rsize = int(map.pmap.stats.resident_count)
2682 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>18s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
2683 print "{:#018x} {:#018x} {:#018x} {:>10d} {:>18d} {:#018x}:{:#018x}".format(map,map.pmap,unsigned(map.size),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end)
2684 vme_list_head = map.hdr.links
2685 vme_ptr_type = GetType('vm_map_entry *')
2686 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:<8s} {:<10s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag.kmod","prot&flags","object","offset")
2687 last_end = unsigned(map.hdr.links.start)
2688 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
2689 if unsigned(vme.links.start) != last_end:
2690 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(unsigned(vme.links.start) - last_end)/page_size)
2691 last_end = unsigned(vme.links.end)
2692 size = unsigned(vme.links.end) - unsigned(vme.links.start)
2693 object = vme.vme_object.vmo_object
2694 if object == 0:
2695 object_str = "{:<#018x}".format(object)
2696 elif vme.is_sub_map:
2697 if object == kern.globals.bufferhdr_map:
2698 object_str = "BUFFERHDR_MAP"
2699 elif object == kern.globals.mb_map:
2700 object_str = "MB_MAP"
2701 elif object == kern.globals.bsd_pageable_map:
2702 object_str = "BSD_PAGEABLE_MAP"
2703 elif object == kern.globals.ipc_kernel_map:
2704 object_str = "IPC_KERNEL_MAP"
2705 elif object == kern.globals.ipc_kernel_copy_map:
2706 object_str = "IPC_KERNEL_COPY_MAP"
2707 elif object == kern.globals.kalloc_map:
2708 object_str = "KALLOC_MAP"
2709 elif object == kern.globals.zone_map:
2710 object_str = "ZONE_MAP"
2711 elif hasattr(kern.globals, 'compressor_map') and object == kern.globals.compressor_map:
2712 object_str = "COMPRESSOR_MAP"
2713 elif hasattr(kern.globals, 'gzalloc_map') and object == kern.globals.gzalloc_map:
2714 object_str = "GZALLOC_MAP"
2715 elif hasattr(kern.globals, 'g_kext_map') and object == kern.globals.g_kext_map:
2716 object_str = "G_KEXT_MAP"
2717 elif hasattr(kern.globals, 'vector_upl_submap') and object == kern.globals.vector_upl_submap:
2718 object_str = "VECTOR_UPL_SUBMAP"
2719 else:
2720 object_str = "submap:{:<#018x}".format(object)
2721 else:
2722 if object == kern.globals.kernel_object:
2723 object_str = "KERNEL_OBJECT"
2724 elif object == kern.globals.vm_submap_object:
2725 object_str = "VM_SUBMAP_OBJECT"
2726 elif object == kern.globals.compressor_object:
2727 object_str = "COMPRESSOR_OBJECT"
2728 else:
2729 object_str = "{:<#018x}".format(object)
2730 offset = unsigned(vme.vme_offset) & ~0xFFF
2731 tag = unsigned(vme.vme_offset & 0xFFF)
2732 vme_flags = ""
2733 if vme.is_sub_map:
2734 vme_flags += "s"
2735 if vme.needs_copy:
2736 vme_flags += "n"
2737 if vme.is_sub_map and vme.use_pmap:
2738 vme_flags += "p"
2739 tagstr = ""
2740 if map.pmap == kern.globals.kernel_pmap:
2741 xsite = Cast(kern.globals.vm_allocation_sites[tag],'OSKextAccount *')
2742 if xsite and xsite.site.flags & 0x0200:
2743 tagstr = ".{:<3d}".format(xsite.loadTag)
2744 print "{:#018x} {:#018x}:{:#018x} {:>10d} {:>3d}{:<4s} {:1d}{:1d}{:<8s} {:<18s} {:<#18x}".format(vme,vme.links.start,vme.links.end,(unsigned(vme.links.end)-unsigned(vme.links.start))/page_size,tag,tagstr,vme.protection,vme.max_protection,vme_flags,object_str,offset)
2745 if (show_pager_info or show_all_shadows) and vme.is_sub_map == 0 and vme.vme_object.vmo_object != 0:
2746 object = vme.vme_object.vmo_object
2747 else:
2748 object = 0
2749 depth = 0
2750 while object != 0:
2751 depth += 1
2752 if show_all_shadows == False and depth != 1 and object.shadow != 0:
2753 offset += unsigned(object.vo_un2.vou_shadow_offset)
2754 object = object.shadow
2755 continue
2756 if object.copy_strategy == 0:
2757 copy_strategy="N"
2758 elif object.copy_strategy == 2:
2759 copy_strategy="D"
2760 elif object.copy_strategy == 4:
2761 copy_strategy="S"
2762 else:
2763 copy_strategy=str(object.copy_strategy)
2764 if object.internal:
2765 internal = "internal"
2766 else:
2767 internal = "external"
2768 pager_string = ""
2769 pager = object.pager
2770 if show_pager_info and pager != 0:
2771 if object.internal:
2772 pager_string = "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
2773 elif unsigned(pager.mo_pager_ops) == vnode_pager_ops_addr:
2774 vnode_pager = Cast(pager,'vnode_pager *')
2775 pager_string = "-> " + GetVnodePath(vnode_pager.vnode_handle)
2776 else:
2777 pager_string = "-> {:s}:{:#018x}".format(pager.mo_pager_ops.memory_object_pager_name, pager.mo_pager_ops)
2778 print "{:>18d} {:#018x}:{:#018x} {:#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} ({:d} {:d} {:d}) {:s}".format(depth,offset,offset+size,object,object.ref_count,object.true_share,copy_strategy,internal,unsigned(object.vo_un1.vou_size)/page_size,object.resident_page_count,object.wired_page_count,pager_string)
2779 # print " #{:<5d} obj {:#018x} ref:{:<6d} ts:{:1d} strat:{:1s} {:s} size:{:<10d} wired:{:<10d} resident:{:<10d} reusable:{:<10d}".format(depth,object,object.ref_count,object.true_share,copy_strategy,internal,object.vo_un1.vou_size/page_size,object.wired_page_count,object.resident_page_count,object.reusable_page_count)
2780 offset += unsigned(object.vo_un2.vou_shadow_offset)
2781 object = object.shadow
2782 if unsigned(map.hdr.links.end) > last_end:
2783 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,map.hdr.links.end,(unsigned(map.hdr.links.end) - last_end)/page_size)
2784 return None
2785
2786 def CountMapTags(map, tagcounts, slow):
2787 page_size = unsigned(kern.globals.page_size)
2788 vme_list_head = map.hdr.links
2789 vme_ptr_type = GetType('vm_map_entry *')
2790 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
2791 object = vme.vme_object.vmo_object
2792 tag = vme.vme_offset & 0xFFF
2793 if object == kern.globals.kernel_object:
2794 count = 0
2795 if not slow:
2796 count = unsigned(vme.links.end - vme.links.start) / page_size
2797 else:
2798 addr = unsigned(vme.links.start)
2799 while addr < unsigned(vme.links.end):
2800 hash_id = _calc_vm_page_hash(object, addr)
2801 page_list = kern.globals.vm_page_buckets[hash_id].page_list
2802 page = _vm_page_unpack_ptr(page_list)
2803 while (page != 0):
2804 vmpage = kern.GetValueFromAddress(page, 'vm_page_t')
2805 if (addr == unsigned(vmpage.offset)) and (object == vm_object_t(_vm_page_unpack_ptr(vmpage.vm_page_object))):
2806 if (not vmpage.local) and (vmpage.wire_count > 0):
2807 count += 1
2808 break
2809 page = _vm_page_unpack_ptr(vmpage.next_m)
2810 addr += page_size
2811 tagcounts[tag] += count
2812 elif vme.is_sub_map:
2813 CountMapTags(Cast(object,'vm_map_t'), tagcounts, slow)
2814 return None
2815
2816 def CountWiredObject(object, tagcounts):
2817 tagcounts[unsigned(object.wire_tag)] += object.wired_page_count
2818 return None
2819
2820 def CountWiredPurgeableGroup(qhead, tagcounts):
2821 for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
2822 CountWiredObject(object, tagcounts)
2823 return None
2824
2825 def CountWiredPurgeableQueue(qhead, tagcounts):
2826 CountWiredPurgeableGroup(qhead.objq[0], tagcounts)
2827 CountWiredPurgeableGroup(qhead.objq[1], tagcounts)
2828 CountWiredPurgeableGroup(qhead.objq[2], tagcounts)
2829 CountWiredPurgeableGroup(qhead.objq[3], tagcounts)
2830 CountWiredPurgeableGroup(qhead.objq[4], tagcounts)
2831 CountWiredPurgeableGroup(qhead.objq[5], tagcounts)
2832 CountWiredPurgeableGroup(qhead.objq[6], tagcounts)
2833 CountWiredPurgeableGroup(qhead.objq[7], tagcounts)
2834
2835 def GetKmodIDName(kmod_id):
2836 kmod_val = kern.globals.kmod
2837 for kmod in IterateLinkedList(kmod_val, 'next'):
2838 if (kmod.id == kmod_id):
2839 return "{:<50s}".format(kmod.name)
2840 return "??"
2841
2842 FixedTags = {
2843 0: "VM_KERN_MEMORY_NONE",
2844 1: "VM_KERN_MEMORY_OSFMK",
2845 2: "VM_KERN_MEMORY_BSD",
2846 3: "VM_KERN_MEMORY_IOKIT",
2847 4: "VM_KERN_MEMORY_LIBKERN",
2848 5: "VM_KERN_MEMORY_OSKEXT",
2849 6: "VM_KERN_MEMORY_KEXT",
2850 7: "VM_KERN_MEMORY_IPC",
2851 8: "VM_KERN_MEMORY_STACK",
2852 9: "VM_KERN_MEMORY_CPU",
2853 10: "VM_KERN_MEMORY_PMAP",
2854 11: "VM_KERN_MEMORY_PTE",
2855 12: "VM_KERN_MEMORY_ZONE",
2856 13: "VM_KERN_MEMORY_KALLOC",
2857 14: "VM_KERN_MEMORY_COMPRESSOR",
2858 15: "VM_KERN_MEMORY_COMPRESSED_DATA",
2859 16: "VM_KERN_MEMORY_PHANTOM_CACHE",
2860 17: "VM_KERN_MEMORY_WAITQ",
2861 18: "VM_KERN_MEMORY_DIAG",
2862 19: "VM_KERN_MEMORY_LOG",
2863 20: "VM_KERN_MEMORY_FILE",
2864 21: "VM_KERN_MEMORY_MBUF",
2865 22: "VM_KERN_MEMORY_UBC",
2866 23: "VM_KERN_MEMORY_SECURITY",
2867 24: "VM_KERN_MEMORY_MLOCK",
2868 25: "VM_KERN_MEMORY_REASON",
2869 26: "VM_KERN_MEMORY_SKYWALK",
2870 27: "VM_KERN_MEMORY_LTABLE",
2871 255:"VM_KERN_MEMORY_ANY",
2872 }
2873
2874 def GetVMKernName(tag):
2875 return FixedTags[tag]
2876
2877 @lldb_command("showvmtags", "S")
2878 def showvmtags(cmd_args=None, cmd_options={}):
2879 """Routine to print out info about kernel wired page allocations
2880 usage: showvmtags
2881 iterates kernel map and vm objects totaling allocations by tag.
2882 usage: showvmtags -S
2883 also iterates kernel object pages individually - slow.
2884 """
2885 slow = False
2886 if "-S" in cmd_options:
2887 slow = True
2888 page_size = unsigned(kern.globals.page_size)
2889 tagcounts = []
2890 tagpeaks = []
2891 for tag in range(256):
2892 tagcounts.append(0)
2893 for tag in range(256):
2894 tagpeaks.append(0)
2895
2896 if kern.globals.vm_tag_active_update:
2897 for tag in range(256):
2898 site = kern.globals.vm_allocation_sites[tag]
2899 if site:
2900 tagcounts[unsigned(tag)] = unsigned(site.total)
2901 tagpeaks[unsigned(tag)] = unsigned(site.peak)
2902 else:
2903 queue_head = kern.globals.vm_objects_wired
2904 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
2905 if object != kern.globals.kernel_object:
2906 CountWiredObject(object, tagcounts)
2907
2908 queue_head = kern.globals.purgeable_nonvolatile_queue
2909 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
2910 CountWiredObject(object, tagcounts)
2911
2912 purgeable_queues = kern.globals.purgeable_queues
2913 CountWiredPurgeableQueue(purgeable_queues[0], tagcounts)
2914 CountWiredPurgeableQueue(purgeable_queues[1], tagcounts)
2915 CountWiredPurgeableQueue(purgeable_queues[2], tagcounts)
2916
2917 CountMapTags(kern.globals.kernel_map, tagcounts, slow)
2918
2919 total = 0
2920 print " {:<7s} {:>7s} {:>7s} {:<50s}".format("tag.kmod","peak","size","name")
2921 for tag in range(256):
2922 if tagcounts[tag]:
2923 total += tagcounts[tag]
2924 tagstr = ""
2925 sitestr = ""
2926 if ((tag <= 27) or (tag == 255)):
2927 sitestr = GetVMKernName(tag)
2928 else:
2929 site = kern.globals.vm_allocation_sites[tag]
2930 if site:
2931 if site.flags & 0x007F:
2932 cstr = addressof(site.subtotals[site.subtotalscount])
2933 sitestr = "{:<50s}".format(str(Cast(cstr, 'char *')))
2934 else:
2935 if site.flags & 0x0200:
2936 xsite = Cast(site,'OSKextAccount *')
2937 tagstr = ".{:<3d}".format(xsite.loadTag)
2938 sitestr = GetKmodIDName(xsite.loadTag)
2939 else:
2940 sitestr = kern.Symbolicate(site)
2941 print " {:>3d}{:<4s} {:>7d}K {:>7d}K {:<50s}".format(tag,tagstr,tagpeaks[tag] / 1024, tagcounts[tag] / 1024,sitestr)
2942 print "Total: {:>7d}K".format(total / 1024)
2943 return None
2944
2945
2946 def FindVMEntriesForVnode(task, vn):
2947 """ returns an array of vme that have the vnode set to defined vnode
2948 each entry in array is of format (vme, start_addr, end_address, protection)
2949 """
2950 retval = []
2951 vmmap = task.map
2952 pmap = vmmap.pmap
2953 pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
2954 debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
2955
2956 if unsigned(pmap) == 0:
2957 return retval
2958 vme_list_head = vmmap.hdr.links
2959 vme_ptr_type = gettype('vm_map_entry *')
2960 for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
2961 #print vme
2962 if unsigned(vme.is_sub_map) == 0 and unsigned(vme.vme_object.vmo_object) != 0:
2963 obj = vme.vme_object.vmo_object
2964 else:
2965 continue
2966
2967 while obj != 0:
2968 if obj.pager != 0:
2969 if obj.internal:
2970 pass
2971 else:
2972 vn_pager = Cast(obj.pager, 'vnode_pager *')
2973 if unsigned(vn_pager.vn_pgr_hdr.mo_pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
2974 retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
2975 obj = obj.shadow
2976 return retval
2977
2978 @lldb_command('showtaskloadinfo')
2979 def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
2980 """ Print the load address and uuid for the process
2981 Usage: (lldb)showtaskloadinfo <task_t>
2982 """
2983 if not cmd_args:
2984 raise ArgumentError("Insufficient arguments")
2985 t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
2986 print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
2987 p = Cast(t.bsd_info, 'struct proc *')
2988 uuid = p.p_uuid
2989 uuid_out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X}".format(a=uuid)
2990 filepath = GetVnodePath(p.p_textvp)
2991 libname = filepath.split('/')[-1]
2992 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
2993 mappings = FindVMEntriesForVnode(t, p.p_textvp)
2994 load_addr = 0
2995 end_addr = 0
2996 for m in mappings:
2997 if m[3] == 5:
2998 load_addr = m[1]
2999 end_addr = m[2]
3000 #print "Load address: %s" % hex(m[1])
3001 print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
3002 return None
3003
3004 @header("{0: <20s} {1: <20s} {2: <20s}".format("vm_page_t", "offset", "object"))
3005 @lldb_command('vmpagelookup')
3006 def VMPageLookup(cmd_args=None):
3007 """ Print the pages in the page bucket corresponding to the provided object and offset.
3008 Usage: (lldb)vmpagelookup <vm_object_t> <vm_offset_t>
3009 """
3010 if cmd_args == None or len(cmd_args) < 2:
3011 raise ArgumentError("Please specify an object and offset.")
3012 format_string = "{0: <#020x} {1: <#020x} {2: <#020x}\n"
3013
3014 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3015 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3016
3017 hash_id = _calc_vm_page_hash(obj, off)
3018
3019 page_list = kern.globals.vm_page_buckets[hash_id].page_list
3020 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(page_list)))
3021
3022 print VMPageLookup.header
3023 page = _vm_page_unpack_ptr(page_list)
3024 while (page != 0) :
3025 pg_t = kern.GetValueFromAddress(page, 'vm_page_t')
3026 print format_string.format(page, pg_t.offset, _vm_page_unpack_ptr(pg_t.vm_page_object))
3027 page = _vm_page_unpack_ptr(pg_t.next_m)
3028
3029
3030
3031 @lldb_command('vmpage_get_phys_page')
3032 def VmPageGetPhysPage(cmd_args=None):
3033 """ return the physical page for a vm_page_t
3034 usage: vm_page_get_phys_page <vm_page_t>
3035 """
3036 if cmd_args == None or len(cmd_args) < 1:
3037 print "Please provide valid vm_page_t. Type help vm_page_get_phys_page for help."
3038 return
3039
3040 page = kern.GetValueFromAddress(cmd_args[0], 'vm_page_t')
3041 phys_page = _vm_page_get_phys_page(page)
3042 print("phys_page = 0x%x\n" % phys_page)
3043
3044
3045 def _vm_page_get_phys_page(page):
3046 if kern.arch == 'x86_64':
3047 return page.phys_page
3048
3049 if page == 0 :
3050 return 0
3051
3052 m = unsigned(page)
3053 if m >= unsigned(kern.globals.vm_page_array_beginning_addr) and m < unsigned(kern.globals.vm_page_array_ending_addr) :
3054 return (m - unsigned(kern.globals.vm_page_array_beginning_addr)) / sizeof('struct vm_page') + unsigned(kern.globals.vm_first_phys_ppnum)
3055
3056 page_with_ppnum = Cast(page, 'uint32_t *')
3057 ppnum_offset = sizeof('struct vm_page') / sizeof('uint32_t')
3058 return page_with_ppnum[ppnum_offset]
3059
3060
3061 @lldb_command('vmpage_unpack_ptr')
3062 def VmPageUnpackPtr(cmd_args=None):
3063 """ unpack a pointer
3064 usage: vm_page_unpack_ptr <packed_ptr>
3065 """
3066 if cmd_args == None or len(cmd_args) < 1:
3067 print "Please provide valid packed pointer argument. Type help vm_page_unpack_ptr for help."
3068 return
3069
3070 packed = kern.GetValueFromAddress(cmd_args[0],'unsigned long')
3071 unpacked = _vm_page_unpack_ptr(packed)
3072 print("unpacked pointer = 0x%x\n" % unpacked)
3073
3074
3075 def _vm_page_unpack_ptr(page):
3076 if kern.ptrsize == 4 :
3077 return page
3078
3079 if page == 0 :
3080 return page
3081
3082 min_addr = kern.globals.vm_min_kernel_and_kext_address
3083 ptr_shift = kern.globals.vm_packed_pointer_shift
3084 ptr_mask = kern.globals.vm_packed_from_vm_pages_array_mask
3085 #INTEL - min_addr = 0xffffff7f80000000
3086 #ARM - min_addr = 0x80000000
3087 #ARM64 - min_addr = 0xffffff8000000000
3088 if unsigned(page) & unsigned(ptr_mask) :
3089 masked_page = (unsigned(page) & ~ptr_mask)
3090 return (unsigned(addressof(kern.globals.vm_pages[masked_page])))
3091 return ((unsigned(page) << unsigned(ptr_shift)) + unsigned(min_addr))
3092
3093 @lldb_command('calcvmpagehash')
3094 def CalcVMPageHash(cmd_args=None):
3095 """ Get the page bucket corresponding to the provided object and offset.
3096 Usage: (lldb)calcvmpagehash <vm_object_t> <vm_offset_t>
3097 """
3098 if cmd_args == None or len(cmd_args) < 2:
3099 raise ArgumentError("Please specify an object and offset.")
3100
3101 obj = kern.GetValueFromAddress(cmd_args[0],'unsigned long long')
3102 off = kern.GetValueFromAddress(cmd_args[1],'unsigned long long')
3103
3104 hash_id = _calc_vm_page_hash(obj, off)
3105
3106 print("hash_id: 0x%x page_list: 0x%x\n" % (unsigned(hash_id), unsigned(kern.globals.vm_page_buckets[hash_id].page_list)))
3107 return None
3108
3109 def _calc_vm_page_hash(obj, off):
3110 bucket_hash = (int) (kern.globals.vm_page_bucket_hash)
3111 hash_mask = (int) (kern.globals.vm_page_hash_mask)
3112
3113 one = (obj * bucket_hash) & 0xFFFFFFFF
3114 two = off >> unsigned(kern.globals.page_shift)
3115 three = two ^ bucket_hash
3116 four = one + three
3117 hash_id = four & hash_mask
3118
3119 return hash_id
3120
3121 VM_PAGE_IS_WIRED = 1
3122
3123 @header("{0: <10s} of {1: <10s} {2: <20s} {3: <20s} {4: <20s} {5: <10s} {6: <5s}\t {7: <28s}\t{8: <50s}".format("index", "total", "vm_page_t", "offset", "next", "phys_page", "wire#", "first bitfield", "second bitfield"))
3124 @lldb_command('vmobjectwalkpages', 'SBNQP:')
3125 def VMObjectWalkPages(cmd_args=None, cmd_options={}):
3126 """ Print the resident pages contained in the provided object. If a vm_page_t is provided as well, we
3127 specifically look for this page, highlighting it in the output or noting if it was not found. For
3128 each page, we confirm that it points to the object. We also keep track of the number of pages we
3129 see and compare this to the object's resident page count field.
3130 Usage:
3131 vmobjectwalkpages <vm_object_t> : Walk and print all the pages for a given object (up to 4K pages by default)
3132 vmobjectwalkpages <vm_object_t> -B : Walk and print all the pages for a given object (up to 4K pages by default), traversing the memq backwards
3133 vmobjectwalkpages <vm_object_t> -N : Walk and print all the pages for a given object, ignore the page limit
3134 vmobjectwalkpages <vm_object_t> -Q : Walk all pages for a given object, looking for known signs of corruption (i.e. q_state == VM_PAGE_IS_WIRED && wire_count == 0)
3135 vmobjectwalkpages <vm_object_t> -P <vm_page_t> : Walk all the pages for a given object, annotate the specified page in the output with ***
3136 vmobjectwalkpages <vm_object_t> -P <vm_page_t> -S : Walk all the pages for a given object, stopping when we find the specified page
3137
3138 """
3139
3140 if (cmd_args == None or len(cmd_args) < 1):
3141 raise ArgumentError("Please specify at minimum a vm_object_t and optionally a vm_page_t")
3142
3143 out_string = ""
3144
3145 obj = kern.GetValueFromAddress(cmd_args[0], 'vm_object_t')
3146
3147 page = 0
3148 if "-P" in cmd_options:
3149 page = kern.GetValueFromAddress(cmd_options['-P'], 'vm_page_t')
3150
3151 stop = 0
3152 if "-S" in cmd_options:
3153 if page == 0:
3154 raise ArgumentError("-S can only be passed when a page is specified with -P")
3155 stop = 1
3156
3157 walk_backwards = False
3158 if "-B" in cmd_options:
3159 walk_backwards = True
3160
3161 quiet_mode = False
3162 if "-Q" in cmd_options:
3163 quiet_mode = True
3164
3165 if not quiet_mode:
3166 print VMObjectWalkPages.header
3167 format_string = "{0: <#10d} of {1: <#10d} {2: <#020x} {3: <#020x} {4: <#020x} {5: <#010x} {6: <#05d}\t"
3168 first_bitfield_format_string = "{0: <#2d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:{7: <#1d}\t"
3169 second_bitfield_format_string = "{0: <#1d}:{1: <#1d}:{2: <#1d}:{3: <#1d}:{4: <#1d}:{5: <#1d}:{6: <#1d}:"
3170 second_bitfield_format_string += "{7: <#1d}:{8: <#1d}:{9: <#1d}:{10: <#1d}:{11: <#1d}:{12: <#1d}:"
3171 second_bitfield_format_string += "{13: <#1d}:{14: <#1d}:{15: <#1d}:{16: <#1d}:{17: <#1d}:{18: <#1d}:{19: <#1d}:"
3172 second_bitfield_format_string += "{20: <#1d}:{21: <#1d}:{22: <#1d}:{23: <#1d}:{24: <#1d}:{25: <#1d}:{26: <#1d}\n"
3173
3174 limit = 4096 #arbitrary limit of number of pages to walk
3175 ignore_limit = 0
3176 if "-N" in cmd_options:
3177 ignore_limit = 1
3178
3179 page_count = 0
3180 res_page_count = unsigned(obj.resident_page_count)
3181 page_found = False
3182 pages_seen = set()
3183
3184 for vmp in IterateQueue(obj.memq, "vm_page_t", "listq", walk_backwards, unpack_ptr_fn=_vm_page_unpack_ptr):
3185 page_count += 1
3186 out_string = ""
3187 if (page != 0 and not(page_found) and vmp == page):
3188 out_string += "******"
3189 page_found = True
3190
3191 if page != 0 or quiet_mode:
3192 if (page_count % 1000) == 0:
3193 print "traversed %d pages ...\n" % (page_count)
3194 else:
3195 out_string += format_string.format(page_count, res_page_count, vmp, vmp.offset, _vm_page_unpack_ptr(vmp.listq.next), _vm_page_get_phys_page(vmp), vmp.wire_count)
3196 out_string += first_bitfield_format_string.format(vmp.vm_page_q_state, vmp.vm_page_in_background, vmp.vm_page_on_backgroundq, vmp.gobbled, vmp.laundry, vmp.no_cache,
3197 vmp.private, vmp.reference)
3198
3199 out_string += second_bitfield_format_string.format(vmp.busy, vmp.wanted, vmp.tabled, vmp.hashed, vmp.fictitious, vmp.clustered,
3200 vmp.pmapped, vmp.xpmapped, vmp.wpmapped, vmp.free_when_done, vmp.absent,
3201 vmp.error, vmp.dirty, vmp.cleaning, vmp.precious, vmp.overwriting,
3202 vmp.restart, vmp.unusual, 0, 0,
3203 vmp.cs_validated, vmp.cs_tainted, vmp.cs_nx, vmp.reusable, vmp.lopage, vmp.slid,
3204 vmp.written_by_kernel)
3205
3206 if (vmp in pages_seen):
3207 print out_string + "cycle detected! we've seen vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " twice. stopping...\n"
3208 return
3209
3210 if (_vm_page_unpack_ptr(vmp.vm_page_object) != unsigned(obj)):
3211 print out_string + " vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " points to different vm_object_t: " + "{0: <#020x}".format(unsigned(_vm_page_unpack_ptr(vmp.vm_page_object)))
3212 return
3213
3214 if (vmp.vm_page_q_state == VM_PAGE_IS_WIRED) and (vmp.wire_count == 0):
3215 print out_string + " page in wired state with wire_count of 0\n"
3216 print "vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + "\n"
3217 print "stopping...\n"
3218 return
3219
3220 if ((vmp.__unused_pageq_bits != 0) or (vmp.__unused_object_bits != 0)):
3221 print out_string + " unused bits not zero for vm_page_t: " + "{0: <#020x}".format(unsigned(vmp)) + " unused__pageq_bits: %d unused_object_bits : %d\n" % (vmp.__unused_pageq_bits,
3222 vmp.__unused_object_bits)
3223 print "stopping...\n"
3224 return
3225
3226 pages_seen.add(vmp)
3227
3228 if False:
3229 hash_id = _calc_vm_page_hash(obj, vmp.offset)
3230 hash_page_list = kern.globals.vm_page_buckets[hash_id].page_list
3231 hash_page = _vm_page_unpack_ptr(hash_page_list)
3232 hash_page_t = 0
3233
3234 while (hash_page != 0):
3235 hash_page_t = kern.GetValueFromAddress(hash_page, 'vm_page_t')
3236 if hash_page_t == vmp:
3237 break
3238 hash_page = _vm_page_unpack_ptr(hash_page_t.next_m)
3239
3240 if (unsigned(vmp) != unsigned(hash_page_t)):
3241 print out_string + "unable to find page: " + "{0: <#020x}".format(unsigned(vmp)) + " from object in kernel page bucket list\n"
3242 print lldb_run_command("vm_page_info %s 0x%x" % (cmd_args[0], unsigned(vmp.offset)))
3243 return
3244
3245 if (page_count >= limit and not(ignore_limit)):
3246 print out_string + "Limit reached (%d pages), stopping..." % (limit)
3247 return
3248
3249 print out_string
3250
3251 if page_found and stop:
3252 print("Object reports resident page count of: %d we stopped after traversing %d and finding the requested page.\n" % (unsigned(obj.res_page_count), unsigned(page_count)))
3253 return
3254
3255 if (page != 0):
3256 print("page found? : %s\n" % page_found)
3257
3258 print("Object reports resident page count of %d, we saw %d pages when we walked the resident list.\n" % (unsigned(obj.resident_page_count), unsigned(page_count)))
3259
3260
3261 @lldb_command("show_all_apple_protect_pagers")
3262 def ShowAllAppleProtectPagers(cmd_args=None):
3263 """Routine to print all apple_protect pagers
3264 usage: show_all_apple_protect_pagers
3265 """
3266 print "{:>3s} {:<3s} {:<18s} {:>5s} {:>5s} {:>6s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s}\n".format("#", "#", "pager", "refs", "ready", "mapped", "mo_control", "object", "offset", "crypto_offset", "crypto_start", "crypto_end")
3267 qhead = kern.globals.apple_protect_pager_queue
3268 qtype = GetType('apple_protect_pager *')
3269 qcnt = kern.globals.apple_protect_pager_count
3270 idx = 0
3271 for pager in IterateQueue(qhead, qtype, "pager_queue"):
3272 idx = idx + 1
3273 show_apple_protect_pager(pager, qcnt, idx)
3274
3275 @lldb_command("show_apple_protect_pager")
3276 def ShowAppleProtectPager(cmd_args=None):
3277 """Routine to print out info about an apple_protect pager
3278 usage: show_apple_protect_pager <pager>
3279 """
3280 if cmd_args == None or len(cmd_args) < 1:
3281 print "Invalid argument.", ShowMap.__doc__
3282 return
3283 pager = kern.GetValueFromAddress(cmd_ars[0], 'apple_protect_pager_t')
3284 show_apple_protect_pager(pager, 1, 1)
3285
3286 def show_apple_protect_pager(pager, qcnt, idx):
3287 object = pager.backing_object
3288 shadow = object.shadow
3289 while shadow != 0:
3290 object = shadow
3291 shadow = object.shadow
3292 vnode_pager = Cast(object.pager,'vnode_pager *')
3293 filename = GetVnodePath(vnode_pager.vnode_handle)
3294 print "{:>3}/{:<3d} {:#018x} {:>5d} {:>5d} {:>6d} {:#018x} {:#018x} {:#018x} {:#018x} {:#018x} {:#018x}\n\tcrypt_info:{:#018x} <decrypt:{:#018x} end:{:#018x} ops:{:#018x} refs:{:<d}>\n\tvnode:{:#018x} {:s}\n".format(idx, qcnt, pager, pager.ref_count, pager.is_ready, pager.is_mapped, pager.pager_control, pager.backing_object, pager.backing_offset, pager.crypto_backing_offset, pager.crypto_start, pager.crypto_end, pager.crypt_info, pager.crypt_info.page_decrypt, pager.crypt_info.crypt_end, pager.crypt_info.crypt_ops, pager.crypt_info.crypt_refcnt, vnode_pager.vnode_handle, filename)
3295
3296 @lldb_command("show_console_ring")
3297 def ShowConsoleRingData(cmd_args=None):
3298 """ Print console ring buffer stats and data
3299 """
3300 cr = kern.globals.console_ring
3301 print "console_ring = {:#018x} buffer = {:#018x} length = {:<5d} used = {:<5d} read_ptr = {:#018x} write_ptr = {:#018x}".format(addressof(cr), cr.buffer, cr.len, cr.used, cr.read_ptr, cr.write_ptr)
3302 pending_data = []
3303 for i in range(unsigned(cr.used)):
3304 idx = ((unsigned(cr.read_ptr) - unsigned(cr.buffer)) + i) % unsigned(cr.len)
3305 pending_data.append("{:c}".format(cr.buffer[idx]))
3306
3307 if pending_data:
3308 print "Data:"
3309 print "".join(pending_data)
3310
3311 # Macro: showjetsamsnapshot
3312
3313 @lldb_command("showjetsamsnapshot", "DA")
3314 def ShowJetsamSnapshot(cmd_args=None, cmd_options={}):
3315 """ Dump entries in the jetsam snapshot table
3316 usage: showjetsamsnapshot [-D] [-A]
3317 Use -D flag to print extra physfootprint details
3318 Use -A flag to print all entries (regardless of valid count)
3319 """
3320
3321 # Not shown are uuid, user_data, cpu_time
3322
3323 global kern
3324
3325 show_footprint_details = False
3326 show_all_entries = False
3327
3328 if "-D" in cmd_options:
3329 show_footprint_details = True
3330
3331 if "-A" in cmd_options:
3332 show_all_entries = True
3333
3334 valid_count = kern.globals.memorystatus_jetsam_snapshot_count
3335 max_count = kern.globals.memorystatus_jetsam_snapshot_max
3336
3337 if (show_all_entries == True):
3338 count = max_count
3339 else:
3340 count = valid_count
3341
3342 print "{:s}".format(valid_count)
3343 print "{:s}".format(max_count)
3344
3345 if int(count) == 0:
3346 print "The jetsam snapshot is empty."
3347 print "Use -A to force dump all entries (regardless of valid count)"
3348 return
3349
3350 # Dumps the snapshot header info
3351 print lldb_run_command('p *memorystatus_jetsam_snapshot')
3352
3353 hdr_format = "{0: >32s} {1: >5s} {2: >4s} {3: >6s} {4: >6s} {5: >20s} {6: >20s} {7: >20s} {8: >5s} {9: >10s} {10: >6s} {11: >6s} {12: >10s} {13: >15s} {14: >15s} {15: >15s} {16: >15s}"
3354 if (show_footprint_details == True):
3355 hdr_format += "{17: >15s} {18: >15s} {19: >12s} {20: >12s} {21: >17s} {22: >10s} {23: >13s} {24: >10s}"
3356
3357
3358 if (show_footprint_details == False):
3359 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'max', 'purgeable', 'lifetimeMax')
3360 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)')
3361 else:
3362 print hdr_format.format('command', 'index', 'pri', 'cid', 'pid', 'starttime', 'killtime', 'idletime', 'kill', '#ents', 'fds', 'gen', 'state', 'footprint', 'max', 'purgeable', 'lifetimeMax', '|| internal', 'internal_comp', 'iokit_mapped', 'purge_nonvol', 'purge_nonvol_comp', 'alt_acct', 'alt_acct_comp', 'page_table')
3363 print hdr_format.format('', '', '', '', '', '(abs)', '(abs)', '(abs)', 'cause', '', '', 'Count', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)')
3364
3365
3366 entry_format = "{e.name: >32s} {index: >5d} {e.priority: >4d} {e.jse_coalition_jetsam_id: >6d} {e.pid: >6d} "\
3367 "{e.jse_starttime: >20d} {e.jse_killtime: >20d} "\
3368 "{e.jse_idle_delta: >20d} {e.killed: >5d} {e.jse_memory_region_count: >10d} "\
3369 "{e.fds: >6d} {e.jse_gencount: >6d} {e.state: >10x} {e.pages: >15d} {e.max_pages: >15d} "\
3370 "{e.purgeable_pages: >15d} {e.max_pages_lifetime: >15d}"
3371
3372 if (show_footprint_details == True):
3373 entry_format += "{e.jse_internal_pages: >15d} "\
3374 "{e.jse_internal_compressed_pages: >15d} "\
3375 "{e.jse_iokit_mapped_pages: >12d} "\
3376 "{e.jse_purgeable_nonvolatile_pages: >12d} "\
3377 "{e.jse_purgeable_nonvolatile_compressed_pages: >17d} "\
3378 "{e.jse_alternate_accounting_pages: >10d} "\
3379 "{e.jse_alternate_accounting_compressed_pages: >13d} "\
3380 "{e.jse_page_table_pages: >10d}"
3381
3382 snapshot_list = kern.globals.memorystatus_jetsam_snapshot.entries
3383 idx = 0
3384 while idx < count:
3385 current_entry = Cast(snapshot_list[idx], 'jetsam_snapshot_entry')
3386 print entry_format.format(index=idx, e=current_entry)
3387 idx +=1
3388 return
3389
3390 # EndMacro: showjetsamsnapshot
3391
3392 # Macro: showvnodecleanblk/showvnodedirtyblk
3393
3394 def _GetBufSummary(buf):
3395 """ Get a summary of important information out of a buf_t.
3396 """
3397 initial = "(struct buf) {0: <#0x} ="
3398
3399 # List all of the fields in this buf summary.
3400 entries = [buf.b_hash, buf.b_vnbufs, buf.b_freelist, buf.b_timestamp, buf.b_whichq,
3401 buf.b_flags, buf.b_lflags, buf.b_error, buf.b_bufsize, buf.b_bcount, buf.b_resid,
3402 buf.b_dev, buf.b_datap, buf.b_lblkno, buf.b_blkno, buf.b_iodone, buf.b_vp,
3403 buf.b_rcred, buf.b_wcred, buf.b_upl, buf.b_real_bp, buf.b_act, buf.b_drvdata,
3404 buf.b_fsprivate, buf.b_transaction, buf.b_dirtyoff, buf.b_dirtyend, buf.b_validoff,
3405 buf.b_validend, buf.b_redundancy_flags, buf.b_proc, buf.b_attr]
3406
3407 # Join an (already decent) string representation of each field
3408 # with newlines and indent the region.
3409 joined_strs = "\n".join([str(i).rstrip() for i in entries]).replace('\n', "\n ")
3410
3411 # Add the total string representation to our title and return it.
3412 out_str = initial.format(int(buf)) + " {\n " + joined_strs + "\n}\n\n"
3413 return out_str
3414
3415 def _ShowVnodeBlocks(dirty=True, cmd_args=None):
3416 """ Display info about all [dirty|clean] blocks in a vnode.
3417 """
3418 if cmd_args == None or len(cmd_args) < 1:
3419 print "Please provide a valid vnode argument."
3420 return
3421
3422 vnodeval = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
3423 list_head = vnodeval.v_cleanblkhd;
3424 if dirty:
3425 list_head = vnodeval.v_dirtyblkhd
3426
3427 print "Blocklist for vnode {}:".format(cmd_args[0])
3428
3429 i = 0
3430 for buf in IterateListEntry(list_head, 'struct buf *', 'b_hash'):
3431 # For each block (buf_t) in the appropriate list,
3432 # ask for a summary and print it.
3433 print "---->\nblock {}: ".format(i) + _GetBufSummary(buf)
3434 i += 1
3435 return
3436
3437 @lldb_command('showvnodecleanblk')
3438 def ShowVnodeCleanBlocks(cmd_args=None):
3439 """ Display info about all clean blocks in a vnode.
3440 usage: showvnodecleanblk <address of vnode>
3441 """
3442 _ShowVnodeBlocks(False, cmd_args)
3443
3444 @lldb_command('showvnodedirtyblk')
3445 def ShowVnodeDirtyBlocks(cmd_args=None):
3446 """ Display info about all dirty blocks in a vnode.
3447 usage: showvnodedirtyblk <address of vnode>
3448 """
3449 _ShowVnodeBlocks(True, cmd_args)
3450
3451 # EndMacro: showvnodecleanblk/showvnodedirtyblk