]> git.saurik.com Git - apple/xnu.git/blob - tools/lldbmacros/memory.py
ff5ce0ae22f06cb739d067636b932c5253c358bc
[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
12 # Macro: memstats
13 @lldb_command('memstats')
14 def Memstats(cmd_args=None):
15 """ 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.
16 """
17 try:
18 print "memorystatus_level: {: >10d}".format(kern.globals.memorystatus_level)
19 except ValueError:
20 pass
21 try:
22 print "memorystatus_available_pages: {: >10d}".format(kern.globals.memorystatus_available_pages)
23 except ValueError:
24 pass
25 print "vm_page_throttled_count: {: >10d}".format(kern.globals.vm_page_throttled_count)
26 print "vm_page_active_count: {: >10d}".format(kern.globals.vm_page_active_count)
27 print "vm_page_inactive_count: {: >10d}".format(kern.globals.vm_page_inactive_count)
28 print "vm_page_wire_count: {: >10d}".format(kern.globals.vm_page_wire_count)
29 print "vm_page_free_count: {: >10d}".format(kern.globals.vm_page_free_count)
30 print "vm_page_purgeable_count: {: >10d}".format(kern.globals.vm_page_purgeable_count)
31 print "vm_page_inactive_target: {: >10d}".format(kern.globals.vm_page_inactive_target)
32 print "vm_page_free_target: {: >10d}".format(kern.globals.vm_page_free_target)
33 print "inuse_ptepages_count: {: >10d}".format(kern.globals.inuse_ptepages_count)
34 print "vm_page_free_reserved: {: >10d}".format(kern.globals.vm_page_free_reserved)
35
36 @xnudebug_test('test_memstats')
37 def TestMemstats(kernel_target, config, lldb_obj, isConnected ):
38 """ Test the functionality of memstats command
39 returns
40 - False on failure
41 - True on success
42 """
43 if not isConnected:
44 print "Target is not connected. Cannot test memstats"
45 return False
46 res = lldb.SBCommandReturnObject()
47 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("memstats", res)
48 result = res.GetOutput()
49 if result.split(":")[1].strip().find('None') == -1 :
50 return True
51 else:
52 return False
53
54 # EndMacro: memstats
55
56 # Macro: showmemorystatus
57 def CalculateLedgerPeak(phys_footprint_entry):
58 """ Internal function to calculate ledger peak value for the given phys footprint entry
59 params: phys_footprint_entry - value representing struct ledger_entry *
60 return: value - representing the ledger peak for the given phys footprint entry
61 """
62 now = kern.globals.sched_tick / 20
63 ledger_peak = phys_footprint_entry.le_credit - phys_footprint_entry.le_debit
64 if (now - phys_footprint_entry._le.le_peaks[0].le_time <= 1) and (phys_footprint_entry._le.le_peaks[0].le_max > ledger_peak):
65 ledger_peak = phys_footprint_entry._le.le_peaks[0].le_max
66 if (now - phys_footprint_entry._le.le_peaks[1].le_time <= 1) and (phys_footprint_entry._le.le_peaks[1].le_max > ledger_peak):
67 ledger_peak = phys_footprint_entry._le.le_peaks[1].le_max
68 return ledger_peak
69
70 @header("{: >8s} {: >22s} {: >22s} {: >11s} {: >11s} {: >12s} {: >10s} {: >13s} {: ^10s} {: >8s} {: <20s}\n".format(
71 'pid', 'effective priority', 'requested priority', 'state', 'user_data', 'physical', 'iokit', 'footprint',
72 'spike', 'limit', 'command'))
73 def GetMemoryStatusNode(proc_val):
74 """ Internal function to get memorystatus information from the given proc
75 params: proc - value representing struct proc *
76 return: str - formatted output information for proc object
77 """
78 out_str = ''
79 task_val = Cast(proc_val.task, 'task *')
80 task_ledgerp = task_val.ledger
81
82 task_physmem_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_mem]
83 task_iokit_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.iokit_mapped]
84 task_phys_footprint_ledger_entry = task_ledgerp.l_entries[kern.globals.task_ledgers.phys_footprint]
85 page_size = kern.globals.page_size
86
87 phys_mem_footprint = (task_physmem_footprint_ledger_entry.le_credit - task_physmem_footprint_ledger_entry.le_debit) / page_size
88 iokit_footprint = (task_iokit_footprint_ledger_entry.le_credit - task_iokit_footprint_ledger_entry.le_debit) / page_size
89 phys_footprint = (task_phys_footprint_ledger_entry.le_credit - task_phys_footprint_ledger_entry.le_debit) / page_size
90 phys_footprint_limit = task_phys_footprint_ledger_entry.le_limit / page_size
91 ledger_peak = CalculateLedgerPeak(task_phys_footprint_ledger_entry)
92 phys_footprint_spike = ledger_peak / page_size
93
94 format_string = '{0: >8d} {1: >22d} {2: >22d} {3: #011x} {4: #011x} {5: >12d} {6: >10d} {7: >13d}'
95 out_str += format_string.format(proc_val.p_pid, proc_val.p_memstat_effectivepriority,
96 proc_val.p_memstat_requestedpriority, proc_val.p_memstat_state, proc_val.p_memstat_userdata,
97 phys_mem_footprint, iokit_footprint, phys_footprint)
98 if phys_footprint != phys_footprint_spike:
99 out_str += "{: ^12d}".format(phys_footprint_spike)
100 else:
101 out_str += "{: ^12s}".format('-')
102 out_str += "{: 8d} {: <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 "{: >91s} {: >10s} {: >13s} {: ^10s} {: >8s}\n".format("(pages)", "(pages)", "(pages)",
114 "(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 # Macro: zprint
129
130 @lldb_type_summary(['zone','zone_t'])
131 @header("{:^18s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s} {:>10s}({:>6s} {:>6s} {:>6s}) {:^14s} {:<20s}".format(
132 'ZONE', 'TOT_SZ', 'PAGE_COUNT', 'ALLOC_ELTS', 'FREE_ELTS', 'FREE_SZ', 'ELT_SZ', 'ALLOC', 'ELTS', 'PGS', 'SLK', 'FLAGS', 'NAME'))
133 def GetZoneSummary(zone):
134 """ Summarize a zone with important information. See help zprint for description of each field
135 params:
136 zone: value - obj representing a zone in kernel
137 returns:
138 str - summary of the zone
139 """
140 out_string = ""
141 format_string = '{:#018x} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:10d} {:6d} {:6d} {:6d} {markings} {name:s} '
142 pagesize = 4096
143
144 free_elements = (zone.cur_size / zone.elem_size) - zone.count
145 free_size = free_elements * zone.elem_size
146
147 alloc_count = zone.alloc_size / zone.elem_size
148 alloc_pages = zone.alloc_size / pagesize
149 alloc_slack = zone.alloc_size % zone.elem_size
150 marks = [
151 ["collectable", "C"],
152 ["expandable", "X"],
153 ["noencrypt", "$"],
154 ["caller_acct", "@"],
155 ["exhaustible", "H"],
156 ["allows_foreign", "F"],
157 ["async_prio_refill", "R"],
158 ["no_callout", "O"],
159 ["zleak_on", "L"],
160 ["doing_alloc", "A"],
161 ["waiting", "W"],
162 ["doing_gc", "G"]
163 ]
164 if kern.arch == 'x86_64':
165 marks.append(["gzalloc_exempt", "M"])
166 marks.append(["alignment_required", "N"])
167
168 markings=""
169 for mark in marks:
170 if zone.__getattr__(mark[0]) :
171 markings+=mark[1]
172 else:
173 markings+=" "
174 out_string += format_string.format(zone, zone.cur_size, zone.page_count,
175 zone.count, free_elements, free_size,
176 zone.elem_size, zone.alloc_size, alloc_count,
177 alloc_pages, alloc_slack, name = zone.zone_name, markings=markings)
178
179 if zone.exhaustible :
180 out_string += "(max: {:d})".format(zone.max_size)
181
182 return out_string
183
184 @lldb_command('zprint')
185 def Zprint(cmd_args=None):
186 """ Routine to print a summary listing of all the kernel zones
187 All columns are printed in decimal
188 Legend:
189 C - collectable
190 X - expandable
191 $ - not encrypted during hibernation
192 @ - allocs and frees are accounted to caller process for KPRVT
193 H - exhaustible
194 F - allows foreign memory (memory not allocated from zone_map)
195 M - gzalloc will avoid monitoring this zone
196 R - will be refilled when below low water mark
197 O - does not allow refill callout to fill zone on noblock allocation
198 N - zone requires alignment (avoids padding this zone for debugging)
199 A - currently trying to allocate more backing memory from kernel_memory_allocate
200 W - another thread is waiting for more memory
201 L - zone is being monitored by zleaks
202 G - currently running GC
203 """
204 global kern
205 print GetZoneSummary.header
206 for zval in kern.zones:
207 print GetZoneSummary(zval)
208
209 @xnudebug_test('test_zprint')
210 def TestZprint(kernel_target, config, lldb_obj, isConnected ):
211 """ Test the functionality of zprint command
212 returns
213 - False on failure
214 - True on success
215 """
216 if not isConnected:
217 print "Target is not connected. Cannot test memstats"
218 return False
219 res = lldb.SBCommandReturnObject()
220 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("zprint", res)
221 result = res.GetOutput()
222 if len(result.split("\n")) > 2:
223 return True
224 else:
225 return False
226
227
228 # EndMacro: zprint
229
230 # Macro: showzfreelist
231
232 def ShowZfreeListHeader(zone):
233 """ Helper routine to print a header for zone freelist.
234 (Since the freelist does not have a custom type, this is not defined as a Type Summary).
235 params:
236 zone:zone_t - Zone object to print header info
237 returns:
238 None
239 """
240
241 scaled_factor = (unsigned(kern.globals.zp_factor) +
242 (unsigned(zone.elem_size) >> unsigned(kern.globals.zp_scale)))
243
244 out_str = ""
245 out_str += "{0: <9s} {1: <12s} {2: <18s} {3: <18s} {4: <6s}\n".format('ELEM_SIZE', 'COUNT', 'NCOOKIE', 'PCOOKIE', 'FACTOR')
246 out_str += "{0: <9d} {1: <12d} 0x{2:0>16x} 0x{3:0>16x} {4: <2d}/{5: <2d}\n\n".format(
247 zone.elem_size, zone.count, kern.globals.zp_nopoison_cookie, kern.globals.zp_poisoned_cookie, zone.zp_count, scaled_factor)
248 out_str += "{0: <7s} {1: <18s} {2: <18s} {3: <18s} {4: <18s} {5: <18s} {6: <14s}\n".format(
249 'NUM', 'ELEM', 'NEXT', 'BACKUP', '^ NCOOKIE', '^ PCOOKIE', 'POISON (PREV)')
250 print out_str
251
252 def ShowZfreeListChain(zone, zfirst, zlimit):
253 """ Helper routine to print a zone free list chain
254 params:
255 zone: zone_t - Zone object
256 zfirst: void * - A pointer to the first element of the free list chain
257 zlimit: int - Limit for the number of elements to be printed by showzfreelist
258 returns:
259 None
260 """
261 current = Cast(zfirst, 'void *')
262 while ShowZfreeList.elts_found < zlimit:
263 ShowZfreeList.elts_found += 1
264 znext = dereference(Cast(current, 'vm_offset_t *'))
265 backup_ptr = kern.GetValueFromAddress((unsigned(Cast(current, 'vm_offset_t')) + unsigned(zone.elem_size) - sizeof('vm_offset_t')), 'vm_offset_t *')
266 backup_val = dereference(backup_ptr)
267 n_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_nopoison_cookie))
268 p_unobfuscated = (unsigned(backup_val) ^ unsigned(kern.globals.zp_poisoned_cookie))
269 poison_str = ''
270 if p_unobfuscated == unsigned(znext):
271 poison_str = "P ({0: <d})".format(ShowZfreeList.elts_found - ShowZfreeList.last_poisoned)
272 ShowZfreeList.last_poisoned = ShowZfreeList.elts_found
273 else:
274 if n_unobfuscated != unsigned(znext):
275 poison_str = "INVALID"
276 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(
277 ShowZfreeList.elts_found, unsigned(current), unsigned(znext), unsigned(backup_val), n_unobfuscated, p_unobfuscated, poison_str)
278 if unsigned(znext) == 0:
279 break
280 current = Cast(znext, 'void *')
281
282 @static_var('elts_found',0)
283 @static_var('last_poisoned',0)
284 @lldb_command('showzfreelist')
285 def ShowZfreeList(cmd_args=None):
286 """ Walk the freelist for a zone, printing out the primary and backup next pointers, the poisoning cookies, and the poisoning status of each element.
287 Usage: showzfreelist <zone> [iterations]
288
289 Will walk up to 50 elements by default, pass a limit in 'iterations' to override.
290 """
291 if not cmd_args:
292 print ShowZfreeList.__doc__
293 return
294 ShowZfreeList.elts_found = 0
295 ShowZfreeList.last_poisoned = 0
296
297 zone = kern.GetValueFromAddress(cmd_args[0], 'struct zone *')
298 zlimit = 50
299 if len(cmd_args) >= 2:
300 zlimit = ArgumentStringToInt(cmd_args[1])
301 ShowZfreeListHeader(zone)
302
303 if unsigned(zone.use_page_list) == 1:
304 if unsigned(zone.allows_foreign) == 1:
305 for free_page_meta in IterateQueue(zone.pages.any_free_foreign, 'struct zone_page_metadata *', 'pages'):
306 if ShowZfreeList.elts_found == zlimit:
307 break
308 zfirst = Cast(free_page_meta.elements, 'void *')
309 if unsigned(zfirst) != 0:
310 ShowZfreeListChain(zone, zfirst, zlimit)
311 for free_page_meta in IterateQueue(zone.pages.intermediate, 'struct zone_page_metadata *', 'pages'):
312 if ShowZfreeList.elts_found == zlimit:
313 break
314 zfirst = Cast(free_page_meta.elements, 'void *')
315 if unsigned(zfirst) != 0:
316 ShowZfreeListChain(zone, zfirst, zlimit)
317 for free_page_meta in IterateQueue(zone.pages.all_free, 'struct zone_page_metadata *', 'pages'):
318 if ShowZfreeList.elts_found == zlimit:
319 break
320 zfirst = Cast(free_page_meta.elements, 'void *')
321 if unsigned(zfirst) != 0:
322 ShowZfreeListChain(zone, zfirst, zlimit)
323 else:
324 zfirst = Cast(zone.free_elements, 'void *')
325 if unsigned(zfirst) != 0:
326 ShowZfreeListChain(zone, zfirst, zlimit)
327
328 if ShowZfreeList.elts_found == zlimit:
329 print "Stopped at {0: <d} elements!".format(zlimit)
330 else:
331 print "Found {0: <d} elements!".format(ShowZfreeList.elts_found)
332
333 # EndMacro: showzfreelist
334
335 # Macro: zstack
336
337 @lldb_command('zstack')
338 def Zstack(cmd_args=None):
339 """ Zone leak debugging: Print the stack trace of log element at <index>. If a <count> is supplied, it prints <count> log elements starting at <index>.
340 Usage: zstack <index> [<count>]
341
342 The suggested usage is to look at indexes below zcurrent and look for common stack traces.
343 The stack trace that occurs the most is probably the cause of the leak. Find the pc of the
344 function calling into zalloc and use the countpcs command to find out how often that pc occurs in the log.
345 The pc occuring in a high percentage of records is most likely the source of the leak.
346
347 The findoldest command is also useful for leak debugging since it identifies the oldest record
348 in the log, which may indicate the leaker.
349 """
350 if not cmd_args:
351 print Zstack.__doc__
352 return
353 if int(kern.globals.log_records) == 0:
354 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
355 return
356 if int(kern.globals.zlog_btlog) == 0:
357 print "Zone logging enabled, but zone has not been initialized yet."
358 return
359
360 count = 1
361 if len(cmd_args) >= 2:
362 count = ArgumentStringToInt(cmd_args[1])
363 zstack_index = unsigned(cmd_args[0])
364 while count and (zstack_index != 0xffffff):
365 zstack_record_offset = zstack_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
366 zstack_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + zstack_record_offset, 'btlog_record_t *')
367 ShowZStackRecord(zstack_record, zstack_index)
368 zstack_index = zstack_record.next
369 count -= 1
370
371 # EndMacro : zstack
372
373 # Macro: findoldest
374
375 @lldb_command('findoldest')
376 def FindOldest(cmd_args=None):
377 """ Zone leak debugging: find and print the oldest record in the log.
378
379 Once it prints a stack trace, find the pc of the caller above all the zalloc, kalloc and
380 IOKit layers. Then use the countpcs command to see how often this caller has allocated
381 memory. A caller with a high percentage of records in the log is probably the leaker.
382 """
383 if int(kern.globals.log_records) == 0:
384 print FindOldest.__doc__
385 return
386 if int(kern.globals.zlog_btlog) == 0:
387 print "Zone logging enabled, but zone has not been initialized yet."
388 return
389 index = kern.globals.zlog_btlog.head
390 if unsigned(index) != 0xffffff:
391 print "Oldest record is at log index: {0: <d}".format(index)
392 Zstack([index])
393 else:
394 print "No Records Present"
395
396 # EndMacro : findoldest
397
398 # Macro: countpcs
399
400 @lldb_command('countpcs')
401 def Countpcs(cmd_args=None):
402 """ Zone leak debugging: search the log and print a count of all log entries that contain the given <pc>
403 in the stack trace.
404 Usage: countpcs <pc>
405
406 This is useful for verifying a suspected <pc> as being the source of
407 the leak. If a high percentage of the log entries contain the given <pc>, then it's most
408 likely the source of the leak. Note that this command can take several minutes to run.
409 """
410 if not cmd_args:
411 print Countpcs.__doc__
412 return
413 if int(kern.globals.log_records) == 0:
414 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
415 return
416 if int(kern.globals.zlog_btlog) == 0:
417 print "Zone logging enabled, but zone has not been initialized yet."
418 return
419
420 cpcs_index = unsigned(kern.globals.zlog_btlog.head)
421 target_pc = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
422 found = 0
423 depth = unsigned(kern.globals.zlog_btlog.btrecord_btdepth)
424
425 while cpcs_index != 0xffffff:
426 cpcs_record_offset = cpcs_index * unsigned(kern.globals.zlog_btlog.btrecord_size)
427 cpcs_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + cpcs_record_offset, 'btlog_record_t *')
428 frame = 0
429 while frame < depth:
430 frame_pc = unsigned(cpcs_record.bt[frame])
431 if frame_pc == target_pc:
432 found += 1
433 break
434 frame += 1
435 cpcs_index = cpcs_record.next
436 print "Occured {0: <d} times in log ({1: <d}{2: <s} of records)".format(found, (found * 100)/unsigned(kern.globals.zlog_btlog.activecount), '%')
437
438 # EndMacro: countpcs
439
440 # Macro: findelem
441
442 @lldb_command('findelem')
443 def FindElem(cmd_args=None):
444 """ Zone corruption debugging: search the log and print out the stack traces for all log entries that
445 refer to the given zone element.
446 Usage: findelem <elem addr>
447
448 When the kernel panics due to a corrupted zone element, get the
449 element address and use this command. This will show you the stack traces of all logged zalloc and
450 zfree operations which tells you who touched the element in the recent past. This also makes
451 double-frees readily apparent.
452 """
453 if not cmd_args:
454 print FindElem.__doc__
455 return
456 if int(kern.globals.log_records) == 0:
457 print "Zone logging not enabled. Add 'zlog=<zone name>' to boot-args."
458 return
459 if int(kern.globals.zlog_btlog) == 0:
460 print "Zone logging enabled, but zone has not been initialized yet."
461 return
462
463 target_element = unsigned(kern.GetValueFromAddress(cmd_args[0], 'void *'))
464 index = unsigned(kern.globals.zlog_btlog.head)
465 prev_op = -1
466
467 while index != 0xffffff:
468 findelem_record_offset = index * unsigned(kern.globals.zlog_btlog.btrecord_size)
469 findelem_record = kern.GetValueFromAddress(unsigned(kern.globals.zlog_btlog.btrecords) + findelem_record_offset, 'btlog_record_t *')
470 if unsigned(findelem_record.element) == target_element:
471 Zstack([index])
472 if int(findelem_record.operation) == prev_op:
473 print "{0: <s} DOUBLE OP! {1: <s}".format(('*' * 8), ('*' * 8))
474 prev_op = int(findelem_record.operation)
475 index = findelem_record.next
476
477 # EndMacro: findelem
478
479 # Macro: btlog_find
480
481 @lldb_command('btlog_find', "AS")
482 def BtlogFind(cmd_args=None, cmd_options={}):
483 """ Search the btlog_t for entries corresponding to the given element.
484 Use -A flag to print all entries.
485 Use -S flag to summarize the count of records
486 Usage: btlog_find <btlog_t> <element>
487 Usage: btlog_find <btlog_t> -A
488 Note: Backtraces will be in chronological order, with oldest entries aged out in FIFO order as needed.
489 """
490 if not cmd_args:
491 raise ArgumentError("Need a btlog_t parameter")
492 btlog = kern.GetValueFromAddress(cmd_args[0], 'btlog_t *')
493 printall = False
494 summarize = False
495 summary_cache = {}
496 target_elem = 0xffffffff
497
498 if "-A" in cmd_options:
499 printall = True
500 else:
501 if not printall and len(cmd_args) < 2:
502 raise ArgumentError("<element> is missing in args. Need a search pointer.")
503 target_elem = unsigned(kern.GetValueFromAddress(cmd_args[1], 'void *'))
504
505 if "-S" in cmd_options:
506 summarize = True
507
508 index = unsigned(btlog.head)
509 progress = 0
510 record_size = unsigned(btlog.btrecord_size)
511 try:
512 while index != 0xffffff:
513 record_offset = index * record_size
514 record = kern.GetValueFromAddress(unsigned(btlog.btrecords) + record_offset, 'btlog_record_t *')
515 if printall or unsigned(record.element) == target_elem:
516 _s = '{0: <s} {2: <#0x} OP {1: <d} {3: <s}'.format(('-' * 8), record.operation, unsigned(record.element), ('-' * 8))
517 _s += GetBtlogBacktrace(btlog.btrecord_btdepth, record)
518 if summarize:
519 if _s not in summary_cache:
520 summary_cache[_s] = 1
521 else:
522 summary_cache[_s] += 1
523 else :
524 print _s
525 index = record.next
526 progress += 1
527 if (progress % 1000) == 0: print '{0: <d} entries searched!\n'.format(progress)
528 except ValueError, e:
529 pass
530
531 if summarize:
532 print "=================== SUMMARY =================="
533 for (k,v) in summary_cache.iteritems():
534 print "Count: %d %s \n " % (v, k)
535 return
536
537 #EndMacro: btlog_find
538
539 #Macro: showzalloc
540
541 @lldb_command('showzalloc')
542 def ShowZalloc(cmd_args=None):
543 """ Prints a zallocation from the zallocations array based off its index and prints the associated symbolicated backtrace.
544 Usage: showzalloc <index>
545 """
546 if not cmd_args:
547 print ShowZalloc.__doc__
548 return
549 if unsigned(kern.globals.zallocations) == 0:
550 print "zallocations array not initialized!"
551 return
552 zallocation = kern.globals.zallocations[ArgumentStringToInt(cmd_args[0])]
553 print zallocation
554 ShowZTrace([str(int(zallocation.za_trace_index))])
555
556 #EndMacro: showzalloc
557
558 #Macro: showztrace
559
560 @lldb_command('showztrace')
561 def ShowZTrace(cmd_args=None):
562 """ Prints the backtrace from the ztraces array at index
563 Usage: showztrace <trace index>
564 """
565 if not cmd_args:
566 print ShowZTrace.__doc__
567 return
568 if unsigned(kern.globals.ztraces) == 0:
569 print "ztraces array not initialized!"
570 return
571 ztrace_addr = kern.globals.ztraces[ArgumentStringToInt(cmd_args[0])]
572 print ztrace_addr
573 ShowZstackTraceHelper(ztrace_addr.zt_stack, ztrace_addr.zt_depth)
574
575 #EndMacro: showztrace
576
577 #Macro: showztraceaddr
578
579 @lldb_command('showztraceaddr')
580 def ShowZTraceAddr(cmd_args=None):
581 """ Prints the struct ztrace passed in.
582 Usage: showztraceaddr <trace address>
583 """
584 if not cmd_args:
585 print ShowZTraceAddr.__doc__
586 return
587 ztrace_ptr = kern.GetValueFromAddress(cmd_args[0], 'struct ztrace *')
588 print dereference(ztrace_ptr)
589 ShowZstackTraceHelper(ztrace_ptr.zt_stack, ztrace_ptr.zt_depth)
590
591 #EndMacro: showztraceaddr
592
593 #Macro: showzstacktrace
594
595 @lldb_command('showzstacktrace')
596 def ShowZstackTrace(cmd_args=None):
597 """ Routine to print a stacktrace stored by OSBacktrace.
598 Usage: showzstacktrace <saved stacktrace> [size]
599
600 size is optional, defaults to 15.
601 """
602 if not cmd_args:
603 print ShowZstackTrace.__doc__
604 return
605 void_ptr_type = gettype('void *')
606 void_double_ptr_type = void_ptr_type.GetPointerType()
607 trace = kern.GetValueFromAddress(cmd_args[0], void_double_ptr_type)
608 trace_size = 15
609 if len(cmd_args) >= 2:
610 trace_size = ArgumentStringToInt(cmd_args[1])
611 ShowZstackTraceHelper(trace, trace_size)
612
613 #EndMacro: showzstacktrace
614
615 def ShowZstackTraceHelper(stack, depth):
616 """ Helper routine for printing a zstack.
617 params:
618 stack: void *[] - An array of pointers representing the Zstack
619 depth: int - The depth of the ztrace stack
620 returns:
621 None
622 """
623 trace_current = 0
624 while trace_current < depth:
625 trace_addr = stack[trace_current]
626 symbol_arr = kern.SymbolicateFromAddress(unsigned(trace_addr))
627 if symbol_arr:
628 symbol_str = str(symbol_arr[0].addr)
629 else:
630 symbol_str = ''
631 print '{0: <#x} {1: <s}'.format(trace_addr, symbol_str)
632 trace_current += 1
633
634 #Macro: showtopztrace
635
636 @lldb_command('showtopztrace')
637 def ShowTopZtrace(cmd_args=None):
638 """ Shows the ztrace with the biggest size.
639 (According to top_ztrace, not by iterating through the hash table)
640 """
641 top_trace = kern.globals.top_ztrace
642 print 'Index: {0: <d}'.format((unsigned(top_trace) - unsigned(kern.globals.ztraces)) / sizeof('struct ztrace'))
643 print dereference(top_trace)
644 ShowZstackTraceHelper(top_trace.zt_stack, top_trace.zt_depth)
645
646 #EndMacro: showtopztrace
647
648 #Macro: showzallocs
649
650 @lldb_command('showzallocs')
651 def ShowZallocs(cmd_args=None):
652 """ Prints all allocations in the zallocations table
653 """
654 if unsigned(kern.globals.zallocations) == 0:
655 print "zallocations array not initialized!"
656 return
657 print '{0: <5s} {1: <18s} {2: <5s} {3: <15s}'.format('INDEX','ADDRESS','TRACE','SIZE')
658 current_index = 0
659 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
660 allocation_count = 0
661 while current_index < max_zallocation:
662 current_zalloc = kern.globals.zallocations[current_index]
663 if int(current_zalloc.za_element) != 0:
664 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))
665 allocation_count += 1
666 current_index += 1
667 print 'Total Allocations: {0: <d}'.format(allocation_count)
668
669 #EndMacro: showzallocs
670
671 #Macro: showzallocsfortrace
672
673 @lldb_command('showzallocsfortrace')
674 def ShowZallocsForTrace(cmd_args=None):
675 """ Prints all allocations pointing to the passed in trace's index into ztraces by looking through zallocations table
676 Usage: showzallocsfortrace <trace index>
677 """
678 if not cmd_args:
679 print ShowZallocsForTrace.__doc__
680 return
681 print '{0: <5s} {1: <18s} {2: <15s}'.format('INDEX','ADDRESS','SIZE')
682 target_index = ArgumentStringToInt(cmd_args[0])
683 current_index = 0
684 max_zallocation = unsigned(kern.globals.zleak_alloc_buckets)
685 allocation_count = 0
686 while current_index < max_zallocation:
687 current_zalloc = kern.globals.zallocations[current_index]
688 if unsigned(current_zalloc.za_element) != 0 and (unsigned(current_zalloc.za_trace_index) == unsigned(target_index)):
689 print '{0: <5d} {1: <#018x} {2: <6d}'.format(current_index, current_zalloc.za_element, current_zalloc.za_size)
690 allocation_count += 1
691 current_index += 1
692 print 'Total Allocations: {0: <d}'.format(allocation_count)
693
694 #EndMacro: showzallocsfortrace
695
696 #Macro: showztraces
697
698 @lldb_command('showztraces')
699 def ShowZTraces(cmd_args=None):
700 """ Prints all traces with size > 0
701 """
702 ShowZTracesAbove([0])
703
704 #EndMacro: showztraces
705
706 #Macro: showztracesabove
707
708 @lldb_command('showztracesabove')
709 def ShowZTracesAbove(cmd_args=None):
710 """ Prints all traces with size greater than X
711 Usage: showztracesabove <size>
712 """
713 if not cmd_args:
714 print ShowZTracesAbove.__doc__
715 return
716 print '{0: <5s} {1: <6s}'.format('INDEX','SIZE')
717 current_index = 0
718 ztrace_count = 0
719 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
720 while current_index < max_ztrace:
721 ztrace_current = kern.globals.ztraces[current_index]
722 if ztrace_current.zt_size > unsigned(cmd_args[0]):
723 print '{0: <5d} {1: <6d}'.format(current_index, int(ztrace_current.zt_size))
724 ztrace_count += 1
725 current_index += 1
726 print 'Total traces: {0: <d}'.format(ztrace_count)
727
728 #EndMacro: showztracesabove
729
730 #Macro: showztracehistogram
731
732 @lldb_command('showztracehistogram')
733 def ShowZtraceHistogram(cmd_args=None):
734 """ Prints the histogram of the ztrace table
735 """
736 print '{0: <5s} {1: <9s} {2: <10s}'.format('INDEX','HIT_COUNT','COLLISIONS')
737 current_index = 0
738 ztrace_count = 0
739 max_ztrace = unsigned(kern.globals.zleak_trace_buckets)
740 while current_index < max_ztrace:
741 ztrace_current = kern.globals.ztraces[current_index]
742 if ztrace_current.zt_hit_count != 0:
743 print '{0: <5d} {1: <9d} {2: <10d}'.format(current_index, ztrace_current.zt_hit_count, ztrace_current.zt_collisions)
744 ztrace_count += 1
745 current_index += 1
746 print 'Total traces: {0: <d}'.format(ztrace_count)
747
748 #EndMacro: showztracehistogram
749
750 #Macro: showzallochistogram
751
752 @lldb_command('showzallochistogram')
753 def ShowZallocHistogram(cmd_args=None):
754 """ Prints the histogram for the zalloc table
755 """
756 print '{0: <5s} {1: <9s}'.format('INDEX','HIT_COUNT')
757 current_index = 0
758 zallocation_count = 0
759 max_ztrace = unsigned(kern.globals.zleak_alloc_buckets)
760 while current_index < max_ztrace:
761 zallocation_current = kern.globals.zallocations[current_index]
762 if zallocation_current.za_hit_count != 0:
763 print '{0: <5d} {1: <9d}'.format(current_index, zallocation_current.za_hit_count)
764 zallocation_count += 1
765 current_index += 1
766 print 'Total Allocations: {0: <d}'.format(zallocation_count)
767
768 #EndMacro: showzallochistogram
769
770 #Macro: showzstats
771
772 @lldb_command('showzstats')
773 def ShowZstats(cmd_args=None):
774 """ Prints the zone leak detection stats
775 """
776 print 'z_alloc_collisions: {0: <d}, z_trace_collisions: {1: <d}'.format(unsigned(kern.globals.z_alloc_collisions), unsigned(kern.globals.z_trace_collisions))
777 print 'z_alloc_overwrites: {0: <d}, z_trace_overwrites: {1: <d}'.format(unsigned(kern.globals.z_alloc_overwrites), unsigned(kern.globals.z_trace_overwrites))
778 print 'z_alloc_recorded: {0: <d}, z_trace_recorded: {1: <d}'.format(unsigned(kern.globals.z_alloc_recorded), unsigned(kern.globals.z_trace_recorded))
779
780 #EndMacro: showzstats
781
782 def GetBtlogBacktrace(depth, zstack_record):
783 """ Helper routine for getting a BT Log record backtrace stack.
784 params:
785 depth:int - The depth of the zstack record
786 zstack_record:btlog_record_t * - A BTLog record
787 returns:
788 str - string with backtrace in it.
789 """
790 out_str = ''
791 frame = 0
792 if not zstack_record:
793 return "Zstack record none!"
794
795 depth_val = unsigned(depth)
796 while frame < depth_val:
797 frame_pc = zstack_record.bt[frame]
798 if not frame_pc or int(frame_pc) == 0:
799 break
800 symbol_arr = kern.SymbolicateFromAddress(frame_pc)
801 if symbol_arr:
802 symbol_str = str(symbol_arr[0].addr)
803 else:
804 symbol_str = ''
805 out_str += "{0: <#0x} <{1: <s}>\n".format(frame_pc, symbol_str)
806 frame += 1
807 return out_str
808
809 def ShowZStackRecord(zstack_record, zstack_index):
810 """ Helper routine for printing a single zstack record
811 params:
812 zstack_record:btlog_record_t * - A BTLog record
813 zstack_index:int - Index for the record in the BTLog table
814 returns:
815 None
816 """
817 out_str = ('-' * 8)
818 if zstack_record.operation == 1:
819 out_str += "ALLOC "
820 else:
821 out_str += "FREE "
822 out_str += "{0: <#0x} : Index {1: <d} {2: <s}\n".format(zstack_record.element, zstack_index, ('-' * 8))
823 print out_str
824 print GetBtlogBacktrace(kern.globals.zlog_btlog.btrecord_btdepth, zstack_record)
825
826 # Macro: showioalloc
827
828 @lldb_command('showioalloc')
829 def ShowIOAllocations(cmd_args=None):
830 """ Show some accounting of memory allocated by IOKit allocators. See ioalloccount man page for details.
831 Routine to display a summary of memory accounting allocated by IOKit allocators.
832 """
833 print "Instance allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_ivars_size, (kern.globals.debug_ivars_size / 1024))
834 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_container_malloc_size, (kern.globals.debug_container_malloc_size / 1024))
835 print "IOMalloc allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomalloc_size, (kern.globals.debug_iomalloc_size / 1024))
836 print "Container allocation = {0: <#0x} = {1: d}K".format(kern.globals.debug_iomallocpageable_size, (kern.globals.debug_iomallocpageable_size / 1024))
837
838
839 # EndMacro: showioalloc
840
841
842
843
844 # Macro: showtaskvme
845 @lldb_command('showtaskvme', "PS")
846 def ShowTaskVmeHelper(cmd_args=None, cmd_options={}):
847 """ Display a summary list of the specified vm_map's entries
848 Usage: showtaskvme <task address> (ex. showtaskvme 0x00ataskptr00 )
849 """
850 show_pager_info = False
851 show_all_shadows = False
852 if "-P" in cmd_options:
853 show_pager_info = True
854 if "-S" in cmd_options:
855 show_all_shadows = True
856 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
857 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
858
859 @lldb_command('showallvme', "PS")
860 def ShowAllVME(cmd_args=None, cmd_options={}):
861 """ Routine to print a summary listing of all the vm map entries
862 Go Through each task in system and show the vm memory regions
863 Use -S flag to show VM object shadow chains
864 Use -P flag to show pager info (mapped file, compressed pages, ...)
865 """
866 show_pager_info = False
867 show_all_shadows = False
868 if "-P" in cmd_options:
869 show_pager_info = True
870 if "-S" in cmd_options:
871 show_all_shadows = True
872 for task in kern.tasks:
873 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
874
875 @lldb_command('showallvm')
876 def ShowAllVM(cmd_args=None):
877 """ Routine to print a summary listing of all the vm maps
878 """
879 for task in kern.tasks:
880 print GetTaskSummary.header + ' ' + GetProcSummary.header
881 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
882 print GetVMMapSummary.header
883 print GetVMMapSummary(task.map)
884
885 @lldb_command("showtaskvm")
886 def ShowTaskVM(cmd_args=None):
887 """ Display info about the specified task's vm_map
888 syntax: (lldb) showtaskvm <task_ptr>
889 """
890 if not cmd_args:
891 print ShowTaskVM.__doc__
892 return False
893 task = kern.GetValueFromAddress(cmd_args[0], 'task *')
894 if not task:
895 print "Unknown arguments."
896 return False
897 print GetTaskSummary.header + ' ' + GetProcSummary.header
898 print GetTaskSummary(task) + ' ' + GetProcSummary(Cast(task.bsd_info, 'proc *'))
899 print GetVMMapSummary.header
900 print GetVMMapSummary(task.map)
901 return True
902
903 @lldb_command('showallvmstats')
904 def ShowAllVMStats(cmd_args=None):
905 """ Print a summary of vm statistics in a table format
906 """
907 vmstats = lambda:None
908 vmstats.wired_count = 0
909 vmstats.resident_count = 0
910 vmstats.resident_max = 0
911 vmstats.internal = 0
912 vmstats.external = 0
913 vmstats.reusable = 0
914 vmstats.compressed = 0
915 vmstats.compressed_peak = 0
916 vmstats.compressed_lifetime = 0
917 vmstats.error = ''
918
919 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:}"
920 print hdr_format.format('pid', 'command', '#ents', 'wired', 'vsize', 'rsize', 'NEW RSIZE', 'max rsize', 'internal', 'external', 'reusable', 'compressed', 'compressed', 'compressed', '')
921 print hdr_format.format('', '', '', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(pages)', '(current)', '(peak)', '(lifetime)', '')
922 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}"
923
924 for task in kern.tasks:
925 proc = Cast(task.bsd_info, 'proc *')
926 vmmap = Cast(task.map, '_vm_map *')
927 vmstats.error = ''
928 vmstats.wired_count = vmmap.pmap.stats.wired_count;
929 vmstats.resident_count = unsigned(vmmap.pmap.stats.resident_count);
930 vmstats.resident_max = vmmap.pmap.stats.resident_max;
931 vmstats.internal = unsigned(vmmap.pmap.stats.internal);
932 vmstats.external = unsigned(vmmap.pmap.stats.external);
933 vmstats.reusable = unsigned(vmmap.pmap.stats.reusable);
934 vmstats.compressed = unsigned(vmmap.pmap.stats.compressed);
935 vmstats.compressed_peak = unsigned(vmmap.pmap.stats.compressed_peak);
936 vmstats.compressed_lifetime = unsigned(vmmap.pmap.stats.compressed_lifetime);
937 vmstats.new_resident_count = vmstats.internal + vmstats.external
938
939 if vmstats.internal < 0:
940 vmstats.error += '*'
941 if vmstats.external < 0:
942 vmstats.error += '*'
943 if vmstats.reusable < 0:
944 vmstats.error += '*'
945 if vmstats.compressed < 0:
946 vmstats.error += '*'
947 if vmstats.compressed_peak < 0:
948 vmstats.error += '*'
949 if vmstats.compressed_lifetime < 0:
950 vmstats.error += '*'
951 if vmstats.new_resident_count +vmstats.reusable != vmstats.resident_count:
952 vmstats.error += '*'
953
954 print entry_format.format(p=proc, m=vmmap, vsize=(unsigned(vmmap.size) >> 12), t=task, s=vmstats)
955
956
957 def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
958 """ Routine to print out a summary listing of all the entries in a vm_map
959 params:
960 task - core.value : a object of type 'task *'
961 returns:
962 None
963 """
964 print "vm_map entries for task " + hex(task)
965 print GetTaskSummary.header
966 print GetTaskSummary(task)
967 if not task.map:
968 print "Task {0: <#020x} has map = 0x0"
969 return None
970 print GetVMMapSummary.header
971 print GetVMMapSummary(task.map)
972 vme_list_head = task.map.hdr.links
973 vme_ptr_type = GetType('vm_map_entry *')
974 print GetVMEntrySummary.header
975 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
976 print GetVMEntrySummary(vme, show_pager_info, show_all_shadows)
977 return None
978
979 @lldb_command("showmap")
980 def ShowMap(cmd_args=None):
981 """ Routine to print out info about the specified vm_map
982 usage: showmap <vm_map>
983 """
984 if cmd_args == None or len(cmd_args) < 1:
985 print "Invalid argument.", ShowMap.__doc__
986 return
987 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
988 print GetVMMapSummary.header
989 print GetVMMapSummary(map_val)
990
991 @lldb_command("showmapvme")
992 def ShowMapVME(cmd_args=None):
993 """Routine to print out info about the specified vm_map and its vm entries
994 usage: showmapvme <vm_map>
995 """
996 if cmd_args == None or len(cmd_args) < 1:
997 print "Invalid argument.", ShowMap.__doc__
998 return
999 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1000 print GetVMMapSummary.header
1001 print GetVMMapSummary(map_val)
1002 vme_list_head = map_val.hdr.links
1003 vme_ptr_type = GetType('vm_map_entry *')
1004 print GetVMEntrySummary.header
1005 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
1006 print GetVMEntrySummary(vme)
1007 return None
1008
1009 @lldb_type_summary(['_vm_map *', 'vm_map_t'])
1010 @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"))
1011 def GetVMMapSummary(vmmap):
1012 """ Display interesting bits from vm_map struct """
1013 out_string = ""
1014 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: >5d} {5: <#020x} {6: <#020x}"
1015 vm_size = uint64_t(vmmap.size).value
1016 resident_pages = 0
1017 if vmmap.pmap != 0: resident_pages = int(vmmap.pmap.stats.resident_count)
1018 out_string += format_string.format(vmmap, vmmap.pmap, vm_size, vmmap.hdr.nentries, resident_pages, vmmap.hint, vmmap.first_free)
1019 return out_string
1020
1021 @lldb_type_summary(['vm_map_entry'])
1022 @header("{0: <20s} {1: <20s} {2: <5s} {3: >7s} {4: <20s} {5: <20s}".format("entry", "start", "prot", "#page", "object", "offset"))
1023 def GetVMEntrySummary(vme):
1024 """ Display vm entry specific information. """
1025 out_string = ""
1026 format_string = "{0: <#020x} {1: <#20x} {2: <1x}{3: <1x}{4: <3s} {5: >7d} {6: <#020x} {7: <#020x}"
1027 vme_protection = int(vme.protection)
1028 vme_max_protection = int(vme.max_protection)
1029 vme_extra_info_str ="SC-Ds"[int(vme.inheritance)]
1030 if int(vme.is_sub_map) != 0 :
1031 vme_extra_info_str +="s"
1032 elif int(vme.needs_copy) != 0 :
1033 vme_extra_info_str +="n"
1034 num_pages = (unsigned(vme.links.end) - unsigned(vme.links.start)) >> 12
1035 out_string += format_string.format(vme, vme.links.start, vme_protection, vme_max_protection, vme_extra_info_str, num_pages, vme.object.vm_object, vme.offset)
1036 return out_string
1037
1038 # EndMacro: showtaskvme
1039 @lldb_command('showmapwired')
1040 def ShowMapWired(cmd_args=None):
1041 """ Routine to print out a summary listing of all the entries with wired pages in a vm_map
1042 """
1043 if cmd_args == None or len(cmd_args) < 1:
1044 print "Invalid argument", ShowMapWired.__doc__
1045 return
1046 map_val = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
1047
1048
1049 @lldb_type_summary(['kmod_info_t *'])
1050 @header("{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: >20s} {6: <30s}".format('kmod_info', 'address', 'size', 'id', 'refs', 'version', 'name'))
1051 def GetKextSummary(kmod):
1052 """ returns a string representation of kext information
1053 """
1054 out_string = ""
1055 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: >3d} {4: >5d} {5: >20s} {6: <30s}"
1056 out_string += format_string.format(kmod, kmod.address, kmod.size, kmod.id, kmod.reference_count, kmod.version, kmod.name)
1057 return out_string
1058
1059 @lldb_type_summary(['uuid_t'])
1060 @header("")
1061 def GetUUIDSummary(uuid):
1062 """ returns a string representation like CA50DA4C-CA10-3246-B8DC-93542489AA26
1063 """
1064 arr = Cast(addressof(uuid), 'uint8_t *')
1065 data = []
1066 for i in range(16):
1067 data.append(int(arr[i]))
1068 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)
1069
1070 @lldb_command('showallkmods')
1071 def ShowAllKexts(cmd_args=None):
1072 """Display a summary listing of all loaded kexts (alias: showallkmods)
1073 """
1074 kmod_val = kern.globals.kmod
1075 print "{: <36s} ".format("UUID") + GetKextSummary.header
1076 kextuuidinfo = GetKextLoadInformation()
1077 for kval in IterateLinkedList(kmod_val, 'next'):
1078 uuid = "........-....-....-....-............"
1079 kaddr = unsigned(kval.address)
1080 for l in kextuuidinfo :
1081 if kaddr == int(l[1],16):
1082 uuid = l[0]
1083 break
1084 print uuid + " " + GetKextSummary(kval)
1085
1086 def GetKextLoadInformation(addr=0):
1087 """ Extract the kext uuid and load address information from the kernel data structure.
1088 params:
1089 addr - int - optional integer that is the address to search for.
1090 returns:
1091 [] - array with each entry of format ( 'UUID', 'Hex Load Address')
1092 """
1093 # because of <rdar://problem/12683084>, we can't find summaries directly
1094 #addr = hex(addressof(kern.globals.gLoadedKextSummaries.summaries))
1095 baseaddr = unsigned(kern.globals.gLoadedKextSummaries) + 0x10
1096 summaries_begin = kern.GetValueFromAddress(baseaddr, 'OSKextLoadedKextSummary *')
1097 total_summaries = int(kern.globals.gLoadedKextSummaries.numSummaries)
1098 kext_version = int(kern.globals.gLoadedKextSummaries.version)
1099 entry_size = 64 + 16 + 8 + 8 + 8 + 4 + 4
1100 if kext_version >= 2 :
1101 entry_size = int(kern.globals.gLoadedKextSummaries.entry_size)
1102 retval = []
1103 for i in range(total_summaries):
1104 tmpaddress = unsigned(summaries_begin) + (i * entry_size)
1105 current_kext = kern.GetValueFromAddress(tmpaddress, 'OSKextLoadedKextSummary *')
1106 if addr != 0 :
1107 if addr == unsigned(current_kext.address):
1108 retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) ))
1109 else:
1110 retval.append((GetUUIDSummary(current_kext.uuid) , hex(current_kext.address), str(current_kext.name) ))
1111
1112 return retval
1113
1114 lldb_alias('showallkexts', 'showallkmods')
1115
1116 def GetOSKextVersion(version_num):
1117 """ returns a string of format 1.2.3x from the version_num
1118 params: version_num - int
1119 return: str
1120 """
1121 if version_num == -1 :
1122 return "invalid"
1123 (MAJ_MULT, MIN_MULT, REV_MULT,STAGE_MULT) = (100000000, 1000000, 10000, 1000)
1124 version = version_num
1125
1126 vers_major = version / MAJ_MULT
1127 version = version - (vers_major * MAJ_MULT)
1128
1129 vers_minor = version / MIN_MULT
1130 version = version - (vers_minor * MIN_MULT)
1131
1132 vers_revision = version / REV_MULT
1133 version = version - (vers_revision * REV_MULT)
1134
1135 vers_stage = version / STAGE_MULT
1136 version = version - (vers_stage * STAGE_MULT)
1137
1138 vers_stage_level = version
1139
1140 out_str = "%d.%d" % (vers_major, vers_minor)
1141 if vers_revision > 0: out_str += ".%d" % vers_revision
1142 if vers_stage == 1 : out_str += "d%d" % vers_stage_level
1143 if vers_stage == 3 : out_str += "a%d" % vers_stage_level
1144 if vers_stage == 5 : out_str += "b%d" % vers_stage_level
1145 if vers_stage == 6 : out_str += "fc%d" % vers_stage_level
1146
1147 return out_str
1148
1149 @lldb_command('showallknownkmods')
1150 def ShowAllKnownKexts(cmd_args=None):
1151 """ Display a summary listing of all kexts known in the system.
1152 This is particularly useful to find if some kext was unloaded before this crash'ed state.
1153 """
1154 kext_count = int(kern.globals.sKextsByID.count)
1155 index = 0
1156 kext_dictionary = kern.globals.sKextsByID.dictionary
1157 print "%d kexts in sKextsByID:" % kext_count
1158 print "{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name')
1159 format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
1160
1161 while index < kext_count:
1162 kext_dict = GetObjectAtIndexFromArray(kext_dictionary, index)
1163 kext_name = str(kext_dict.key.string)
1164 osk = Cast(kext_dict.value, 'OSKext *')
1165 if int(osk.flags.loaded) :
1166 load_addr = "{0: <#020x}".format(osk.kmod_info)
1167 id = "{0: >5d}".format(osk.loadTag)
1168 else:
1169 load_addr = "------"
1170 id = "--"
1171 version_num = unsigned(osk.version)
1172 version = GetOSKextVersion(version_num)
1173 print format_string.format(osk, load_addr, id, version, kext_name)
1174 index += 1
1175
1176 return
1177
1178 @lldb_command('showkmodaddr')
1179 def ShowKmodAddr(cmd_args=[]):
1180 """ Given an address, print the offset and name for the kmod containing it
1181 Syntax: (lldb) showkmodaddr <addr>
1182 """
1183 if len(cmd_args) < 1:
1184 raise ArgumentError("Insufficient arguments")
1185
1186 addr = ArgumentStringToInt(cmd_args[0])
1187 kmod_val = kern.globals.kmod
1188 for kval in IterateLinkedList(kmod_val, 'next'):
1189 if addr >= unsigned(kval.address) and addr <= (unsigned(kval.address) + unsigned(kval.size)):
1190 print GetKextSummary.header
1191 print GetKextSummary(kval) + " offset = {0: #0x}".format((addr - unsigned(kval.address)))
1192 return True
1193 return False
1194
1195 @lldb_command('addkext','AF:N:')
1196 def AddKextSyms(cmd_args=[], cmd_options={}):
1197 """ Add kext symbols into lldb.
1198 This command finds symbols for a uuid and load the required executable
1199 Usage:
1200 addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
1201 addkext -F <abs/path/to/executable> <load_address> : Load kext executable at specified load address
1202 addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
1203 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
1204 addkext all : Will load all the kext symbols - SLOW
1205 """
1206
1207
1208 if "-F" in cmd_options:
1209 exec_path = cmd_options["-F"]
1210 exec_full_path = ResolveFSPath(exec_path)
1211 if not os.path.exists(exec_full_path):
1212 raise ArgumentError("Unable to resolve {:s}".format(exec_path))
1213
1214 if not os.path.isfile(exec_full_path):
1215 raise ArgumentError("Path is {:s} not a filepath. \nPlease check that path points to executable.\
1216 \nFor ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.\
1217 \nNote: LLDB does not support adding kext based on directory paths like gdb used to.".format(exec_path))
1218 if not os.access(exec_full_path, os.X_OK):
1219 raise ArgumentError("Path is {:s} not an executable file".format(exec_path))
1220
1221 slide_value = None
1222 if cmd_args:
1223 slide_value = cmd_args[0]
1224 debuglog("loading slide value from user input %s" % cmd_args[0])
1225
1226 filespec = lldb.SBFileSpec(exec_full_path, False)
1227 print "target modules add %s" % exec_full_path
1228 print lldb_run_command("target modules add %s" % exec_full_path)
1229 loaded_module = LazyTarget.GetTarget().FindModule(filespec)
1230 if loaded_module.IsValid():
1231 uuid_str = loaded_module.GetUUIDString()
1232 debuglog("added module %s with uuid %s" % (exec_full_path, uuid_str))
1233 if slide_value is None:
1234 all_kexts_info = GetKextLoadInformation()
1235 for k in all_kexts_info:
1236 debuglog(k[0])
1237 if k[0].lower() == uuid_str.lower():
1238 slide_value = k[1]
1239 debuglog("found the slide %s for uuid %s" % (k[1], k[0]))
1240
1241 if slide_value is None:
1242 raise ArgumentError("Unable to find load address for module described at %s " % exec_full_path)
1243 load_cmd = "target modules load --file %s --slide %s" % (exec_full_path, str(slide_value))
1244 print load_cmd
1245 print lldb_run_command(load_cmd)
1246 kern.symbolicator = None
1247 return True
1248
1249 all_kexts_info = GetKextLoadInformation()
1250
1251 if "-N" in cmd_options:
1252 kext_name = cmd_options["-N"]
1253 kext_name_matches = GetLongestMatchOption(kext_name, [str(x[2]) for x in all_kexts_info], True)
1254 if len(kext_name_matches) != 1 and "-A" not in cmd_options:
1255 print "Ambiguous match for name: {:s}".format(kext_name)
1256 if len(kext_name_matches) > 0:
1257 print "Options are:\n\t" + "\n\t".join(kext_name_matches)
1258 return
1259 debuglog("matched the kext to name %s and uuid %s" % (kext_name_matches[0], kext_name))
1260 for cur_knm in kext_name_matches:
1261 for x in all_kexts_info:
1262 if cur_knm == x[2]:
1263 cur_uuid = x[0].lower()
1264 print "Fetching dSYM for {:s}".format(cur_uuid)
1265 info = dsymForUUID(cur_uuid)
1266 if info and 'DBGSymbolRichExecutable' in info:
1267 print "Adding dSYM ({0:s}) for {1:s}".format(cur_uuid, info['DBGSymbolRichExecutable'])
1268 addDSYM(cur_uuid, info)
1269 loadDSYM(cur_uuid, int(x[1],16))
1270 else:
1271 print "Failed to get symbol info for {:s}".format(cur_uuid)
1272 break
1273 kern.symbolicator = None
1274 return
1275
1276 if len(cmd_args) < 1:
1277 raise ArgumentError("No arguments specified.")
1278
1279 uuid = cmd_args[0].lower()
1280
1281 load_all_kexts = False
1282 if uuid == "all":
1283 load_all_kexts = True
1284
1285 if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
1286 raise ArgumentError("Unknown argument {:s}".format(uuid))
1287
1288 for k_info in all_kexts_info:
1289 cur_uuid = k_info[0].lower()
1290 if load_all_kexts or (uuid == cur_uuid):
1291 print "Fetching dSYM for %s" % cur_uuid
1292 info = dsymForUUID(cur_uuid)
1293 if info and 'DBGSymbolRichExecutable' in info:
1294 print "Adding dSYM (%s) for %s" % (cur_uuid, info['DBGSymbolRichExecutable'])
1295 addDSYM(cur_uuid, info)
1296 loadDSYM(cur_uuid, int(k_info[1],16))
1297 else:
1298 print "Failed to get symbol info for %s" % cur_uuid
1299 #end of for loop
1300 kern.symbolicator = None
1301 return True
1302
1303
1304
1305 lldb_alias('showkmod', 'showkmodaddr')
1306 lldb_alias('showkext', 'showkmodaddr')
1307 lldb_alias('showkextaddr', 'showkmodaddr')
1308
1309 @lldb_type_summary(['mount *'])
1310 @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'))
1311 def GetMountSummary(mount):
1312 """ Display a summary of mount on the system
1313 """
1314 out_string = ("{mnt: <#020x} {mnt.mnt_data: <#020x} {mnt.mnt_devvp: <#020x} {mnt.mnt_flag: <#012x} " +
1315 "{mnt.mnt_kern_flag: <#012x} {mnt.mnt_lflag: <#012x} {vfs.f_fstypename: >6s} " +
1316 "{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'))
1317 return out_string
1318
1319 @lldb_command('showallmounts')
1320 def ShowAllMounts(cmd_args=None):
1321 """ Print all mount points
1322 """
1323 mntlist = kern.globals.mountlist
1324 print GetMountSummary.header
1325 for mnt in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1326 print GetMountSummary(mnt)
1327 return
1328
1329 lldb_alias('ShowAllVols', 'showallmounts')
1330
1331 @lldb_command('systemlog')
1332 def ShowSystemLog(cmd_args=None):
1333 """ Display the kernel's printf ring buffer """
1334 msgbufp = kern.globals.msgbufp
1335 msg_size = int(msgbufp.msg_size)
1336 msg_bufx = int(msgbufp.msg_bufx)
1337 msg_bufr = int(msgbufp.msg_bufr)
1338 msg_bufc = msgbufp.msg_bufc
1339 msg_bufc_data = msg_bufc.GetSBValue().GetPointeeData(0, msg_size)
1340
1341 # the buffer is circular; start at the write pointer to end,
1342 # then from beginning to write pointer
1343 line = ''
1344 err = lldb.SBError()
1345 for i in range(msg_bufx, msg_size) + range(0, msg_bufx) :
1346 err.Clear()
1347 cbyte = msg_bufc_data.GetUnsignedInt8(err, i)
1348 if not err.Success() :
1349 raise ValueError("Failed to read character at offset " + str(i) + ": " + err.GetCString())
1350 c = chr(cbyte)
1351 if c == '\0' :
1352 continue
1353 elif c == '\n' :
1354 print line
1355 line = ''
1356 else :
1357 line += c
1358
1359 if len(line) > 0 :
1360 print line
1361
1362 return
1363
1364 @static_var('output','')
1365 def _GetVnodePathName(vnode, vnodename):
1366 """ Internal function to get vnode path string from vnode structure.
1367 params:
1368 vnode - core.value
1369 vnodename - str
1370 returns Nothing. The output will be stored in the static variable.
1371 """
1372 if not vnode:
1373 return
1374 if int(vnode.v_flag) & 0x1 and int(hex(vnode.v_mount), 16) !=0:
1375 if int(vnode.v_mount.mnt_vnodecovered):
1376 _GetVnodePathName(vnode.v_mount.mnt_vnodecovered, str(vnode.v_mount.mnt_vnodecovered.v_name) )
1377 else:
1378 _GetVnodePathName(vnode.v_parent, str(vnode.v_parent.v_name))
1379 _GetVnodePathName.output += "/%s" % vnodename
1380
1381 def GetVnodePath(vnode):
1382 """ Get string representation of the vnode
1383 params: vnodeval - value representing vnode * in the kernel
1384 return: str - of format /path/to/something
1385 """
1386 out_str = ''
1387 if vnode:
1388 if (int(vnode.v_flag) & 0x000001) and int(hex(vnode.v_mount), 16) != 0 and (int(vnode.v_mount.mnt_flag) & 0x00004000) :
1389 out_str += "/"
1390 else:
1391 _GetVnodePathName.output = ''
1392 if abs(vnode.v_name) != 0:
1393 _GetVnodePathName(vnode, str(vnode.v_name))
1394 out_str += _GetVnodePathName.output
1395 else:
1396 out_str += 'v_name = NULL'
1397 _GetVnodePathName.output = ''
1398 return out_str
1399
1400
1401 @lldb_command('showvnodepath')
1402 def ShowVnodePath(cmd_args=None):
1403 """ Prints the path for a vnode
1404 usage: showvnodepath <vnode>
1405 """
1406 if cmd_args != None and len(cmd_args) > 0 :
1407 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1408 if vnode_val:
1409 print GetVnodePath(vnode_val)
1410 return
1411
1412 # Macro: showvnodedev
1413 def GetVnodeDevInfo(vnode):
1414 """ Internal function to get information from the device type vnodes
1415 params: vnode - value representing struct vnode *
1416 return: str - formatted output information for block and char vnode types passed as param
1417 """
1418 vnodedev_output = ""
1419 vblk_type = GetEnumValue('vtype::VBLK')
1420 vchr_type = GetEnumValue('vtype::VCHR')
1421 if (vnode.v_type == vblk_type) or (vnode.v_type == vchr_type):
1422 devnode = Cast(vnode.v_data, 'devnode_t *')
1423 devnode_dev = devnode.dn_typeinfo.dev
1424 devnode_major = (devnode_dev >> 24) & 0xff
1425 devnode_minor = devnode_dev & 0x00ffffff
1426
1427 # boilerplate device information for a vnode
1428 vnodedev_output += "Device Info:\n\t vnode:\t\t{:#x}".format(vnode)
1429 vnodedev_output += "\n\t type:\t\t"
1430 if (vnode.v_type == vblk_type):
1431 vnodedev_output += "VBLK"
1432 if (vnode.v_type == vchr_type):
1433 vnodedev_output += "VCHR"
1434 vnodedev_output += "\n\t name:\t\t{:<s}".format(vnode.v_name)
1435 vnodedev_output += "\n\t major, minor:\t{:d},{:d}".format(devnode_major, devnode_minor)
1436 vnodedev_output += "\n\t mode\t\t0{:o}".format(unsigned(devnode.dn_mode))
1437 vnodedev_output += "\n\t owner (u,g):\t{:d} {:d}".format(devnode.dn_uid, devnode.dn_gid)
1438
1439 # decode device specific data
1440 vnodedev_output += "\nDevice Specific Information:\t"
1441 if (vnode.v_type == vblk_type):
1442 vnodedev_output += "Sorry, I do not know how to decode block devices yet!"
1443 vnodedev_output += "\nMaybe you can write me!"
1444
1445 if (vnode.v_type == vchr_type):
1446 # Device information; this is scanty
1447 # range check
1448 if (devnode_major > 42) or (devnode_major < 0):
1449 vnodedev_output += "Invalid major #\n"
1450 # static assignments in conf
1451 elif (devnode_major == 0):
1452 vnodedev_output += "Console mux device\n"
1453 elif (devnode_major == 2):
1454 vnodedev_output += "Current tty alias\n"
1455 elif (devnode_major == 3):
1456 vnodedev_output += "NULL device\n"
1457 elif (devnode_major == 4):
1458 vnodedev_output += "Old pty slave\n"
1459 elif (devnode_major == 5):
1460 vnodedev_output += "Old pty master\n"
1461 elif (devnode_major == 6):
1462 vnodedev_output += "Kernel log\n"
1463 elif (devnode_major == 12):
1464 vnodedev_output += "Memory devices\n"
1465 # Statically linked dynamic assignments
1466 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptmx_open')):
1467 vnodedev_output += "Cloning pty master not done\n"
1468 #GetVnodeDevCpty(devnode_major, devnode_minor)
1469 elif unsigned(kern.globals.cdevsw[devnode_major].d_open) == unsigned(kern.GetLoadAddressForSymbol('ptsd_open')):
1470 vnodedev_output += "Cloning pty slave not done\n"
1471 #GetVnodeDevCpty(devnode_major, devnode_minor)
1472 else:
1473 vnodedev_output += "RESERVED SLOT\n"
1474 else:
1475 vnodedev_output += "{:#x} is not a device".format(vnode)
1476 return vnodedev_output
1477
1478 @lldb_command('showvnodedev')
1479 def ShowVnodeDev(cmd_args=None):
1480 """ Routine to display details of all vnodes of block and character device types
1481 Usage: showvnodedev <address of vnode>
1482 """
1483 if not cmd_args:
1484 print "No arguments passed"
1485 print ShowVnodeDev.__doc__
1486 return False
1487 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1488 if not vnode_val:
1489 print "unknown arguments:", str(cmd_args)
1490 return False
1491 print GetVnodeDevInfo(vnode_val)
1492
1493 # EndMacro: showvnodedev
1494
1495 # Macro: showvnodelocks
1496 def GetVnodeLock(lockf):
1497 """ Internal function to get information from the given advisory lock
1498 params: lockf - value representing v_lockf member in struct vnode *
1499 return: str - formatted output information for the advisory lock
1500 """
1501 vnode_lock_output = ''
1502 lockf_flags = lockf.lf_flags
1503 lockf_type = lockf.lf_type
1504 if lockf_flags & 0x20:
1505 vnode_lock_output += ("{: <8s}").format('flock')
1506 if lockf_flags & 0x40:
1507 vnode_lock_output += ("{: <8s}").format('posix')
1508 if lockf_flags & 0x80:
1509 vnode_lock_output += ("{: <8s}").format('prov')
1510 if lockf_flags & 0x10:
1511 vnode_lock_output += ("{: <4s}").format('W')
1512 else:
1513 vnode_lock_output += ("{: <4s}").format('.')
1514
1515 # POSIX file vs advisory range locks
1516 if lockf_flags & 0x40:
1517 lockf_proc = Cast(lockf.lf_id, 'proc *')
1518 vnode_lock_output += ("PID {: <18d}").format(lockf_proc.p_pid)
1519 else:
1520 vnode_lock_output += ("ID {: <#019x}").format(int(lockf.lf_id))
1521
1522 # lock type
1523 if lockf_type == 1:
1524 vnode_lock_output += ("{: <12s}").format('shared')
1525 else:
1526 if lockf_type == 3:
1527 vnode_lock_output += ("{: <12s}").format('exclusive')
1528 else:
1529 if lockf_type == 2:
1530 vnode_lock_output += ("{: <12s}").format('unlock')
1531 else:
1532 vnode_lock_output += ("{: <12s}").format('unknown')
1533
1534 # start and stop values
1535 vnode_lock_output += ("{: #018x} ..").format(lockf.lf_start)
1536 vnode_lock_output += ("{: #018x}\n").format(lockf.lf_end)
1537 return vnode_lock_output
1538
1539 @header("{0: <3s} {1: <7s} {2: <3s} {3: <21s} {4: <11s} {5: ^19s} {6: ^17s}".format('*', 'type', 'W', 'held by', 'lock type', 'start', 'end'))
1540 def GetVnodeLocksSummary(vnode):
1541 """ Internal function to get summary of advisory locks for the given vnode
1542 params: vnode - value representing the vnode object
1543 return: str - formatted output information for the summary of advisory locks
1544 """
1545 out_str = ''
1546 if vnode:
1547 lockf_list = vnode.v_lockf
1548 for lockf_itr in IterateLinkedList(lockf_list, 'lf_next'):
1549 out_str += ("{: <4s}").format('H')
1550 out_str += GetVnodeLock(lockf_itr)
1551 lockf_blocker = lockf_itr.lf_blkhd.tqh_first
1552 while lockf_blocker:
1553 out_str += ("{: <4s}").format('>')
1554 out_str += GetVnodeLock(lockf_blocker)
1555 lockf_blocker = lockf_blocker.lf_block.tqe_next
1556 return out_str
1557
1558 @lldb_command('showvnodelocks')
1559 def ShowVnodeLocks(cmd_args=None):
1560 """ Routine to display list of advisory record locks for the given vnode address
1561 Usage: showvnodelocks <address of vnode>
1562 """
1563 if not cmd_args:
1564 print "No arguments passed"
1565 print ShowVnodeLocks.__doc__
1566 return False
1567 vnode_val = kern.GetValueFromAddress(cmd_args[0], 'vnode *')
1568 if not vnode_val:
1569 print "unknown arguments:", str(cmd_args)
1570 return False
1571 print GetVnodeLocksSummary.header
1572 print GetVnodeLocksSummary(vnode_val)
1573
1574 # EndMacro: showvnodelocks
1575
1576 # Macro: showproclocks
1577
1578 @lldb_command('showproclocks')
1579 def ShowProcLocks(cmd_args=None):
1580 """ Routine to display list of advisory record locks for the given process
1581 Usage: showproclocks <address of proc>
1582 """
1583 if not cmd_args:
1584 print "No arguments passed"
1585 print ShowProcLocks.__doc__
1586 return False
1587 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *')
1588 if not proc:
1589 print "unknown arguments:", str(cmd_args)
1590 return False
1591 out_str = ''
1592 proc_filedesc = proc.p_fd
1593 fd_lastfile = proc_filedesc.fd_lastfile
1594 fd_ofiles = proc_filedesc.fd_ofiles
1595 count = 0
1596 seen = 0
1597 while count <= fd_lastfile:
1598 if fd_ofiles[count]:
1599 fglob = fd_ofiles[count].f_fglob
1600 fo_type = fglob.fg_ops.fo_type
1601 if fo_type == 1:
1602 fg_data = fglob.fg_data
1603 fg_vnode = Cast(fg_data, 'vnode *')
1604 name = fg_vnode.v_name
1605 lockf_itr = fg_vnode.v_lockf
1606 if lockf_itr:
1607 if not seen:
1608 print GetVnodeLocksSummary.header
1609 seen = seen + 1
1610 out_str += ("\n( fd {:d}, name ").format(count)
1611 if not name:
1612 out_str += "(null) )\n"
1613 else:
1614 out_str += "{:s} )\n".format(name)
1615 print out_str
1616 print GetVnodeLocksSummary(fg_vnode)
1617 count = count + 1
1618 print "\n{0: d} total locks for {1: #018x}".format(seen, proc)
1619
1620 # EndMacro: showproclocks
1621
1622 @lldb_type_summary(['vnode_t', 'vnode *'])
1623 @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'))
1624 def GetVnodeSummary(vnode):
1625 """ Get a summary of important information out of vnode
1626 """
1627 out_str = ''
1628 format_string = "{0: <#020x} {1: >8d} {2: >8d} {3: <#020x} {4: <6s} {5: <#020x} {6: <6s} {7: <6s} {8: <35s}"
1629 usecount = int(vnode.v_usecount)
1630 iocount = int(vnode.v_iocount)
1631 v_data_ptr = int(hex(vnode.v_data), 16)
1632 vtype = int(vnode.v_type)
1633 vtype_str = "%d" % vtype
1634 vnode_types = ['VNON', 'VREG', 'VDIR', 'VBLK', 'VCHR', 'VLNK', 'VSOCK', 'VFIFO', 'VBAD', 'VSTR', 'VCPLX'] # see vnode.h for enum type definition
1635 if vtype >= 0 and vtype < len(vnode_types):
1636 vtype_str = vnode_types[vtype]
1637 parent_ptr = int(hex(vnode.v_parent), 16)
1638 name_ptr = int(hex(vnode.v_name), 16)
1639 name =""
1640 if name_ptr != 0:
1641 name = str(vnode.v_name)
1642 elif int(vnode.v_tag) == 16 :
1643 cnode = Cast(vnode.v_data, 'cnode *')
1644 name = "hfs: %s" % str( Cast(cnode.c_desc.cd_nameptr, 'char *'))
1645 mapped = '-'
1646 csblob_version = '-'
1647 if (vtype == 1) and (vnode.v_un.vu_ubcinfo != 0):
1648 csblob_version = '{: <6d}'.format(vnode.v_un.vu_ubcinfo.cs_add_gen)
1649 # Check to see if vnode is mapped/unmapped
1650 if (vnode.v_un.vu_ubcinfo.ui_flags & 0x8) != 0:
1651 mapped = '1'
1652 else:
1653 mapped = '0'
1654 out_str += format_string.format(vnode, usecount, iocount, v_data_ptr, vtype_str, parent_ptr, mapped, csblob_version, name)
1655 return out_str
1656
1657 @lldb_command('showallvnodes')
1658 def ShowAllVnodes(cmd_args=None):
1659 """ Display info about all vnodes
1660 """
1661 mntlist = kern.globals.mountlist
1662 print GetVnodeSummary.header
1663 for mntval in IterateTAILQ_HEAD(mntlist, 'mnt_list'):
1664 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1665 print GetVnodeSummary(vnodeval)
1666 return
1667
1668 @lldb_command('showvnode')
1669 def ShowVnode(cmd_args=None):
1670 """ Display info about one vnode
1671 usage: showvnode <vnode>
1672 """
1673 if cmd_args == None or len(cmd_args) < 1:
1674 print "Please provide valid vnode argument. Type help showvnode for help."
1675 return
1676 vnodeval = kern.GetValueFromAddress(cmd_args[0],'vnode *')
1677 print GetVnodeSummary.header
1678 print GetVnodeSummary(vnodeval)
1679
1680 @lldb_command('showvolvnodes')
1681 def ShowVolVnodes(cmd_args=None):
1682 """ Display info about all vnodes of a given mount_t
1683 """
1684 if cmd_args == None or len(cmd_args) < 1:
1685 print "Please provide a valide mount_t argument. Try 'help showvolvnodes' for help"
1686 return
1687 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
1688 print GetVnodeSummary.header
1689 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1690 print GetVnodeSummary(vnodeval)
1691 return
1692
1693 @lldb_command('showvolbusyvnodes')
1694 def ShowVolBusyVnodes(cmd_args=None):
1695 """ Display info about busy (iocount!=0) vnodes of a given mount_t
1696 """
1697 if cmd_args == None or len(cmd_args) < 1:
1698 print "Please provide a valide mount_t argument. Try 'help showvolbusyvnodes' for help"
1699 return
1700 mntval = kern.GetValueFromAddress(cmd_args[0], 'mount_t')
1701 print GetVnodeSummary.header
1702 for vnodeval in IterateTAILQ_HEAD(mntval.mnt_vnodelist, 'v_mntvnodes'):
1703 if int(vnodeval.v_iocount) != 0:
1704 print GetVnodeSummary(vnodeval)
1705
1706 @lldb_command('showallbusyvnodes')
1707 def ShowAllBusyVnodes(cmd_args=None):
1708 """ Display info about all busy (iocount!=0) vnodes
1709 """
1710 mntlistval = kern.globals.mountlist
1711 for mntval in IterateTAILQ_HEAD(mntlistval, 'mnt_list'):
1712 ShowVolBusyVnodes([hex(mntval)])
1713
1714 @lldb_command('print_vnode')
1715 def PrintVnode(cmd_args=None):
1716 """ Prints out the fields of a vnode struct
1717 Usage: print_vnode <vnode>
1718 """
1719 if not cmd_args:
1720 print "Please provide valid vnode argument. Type help print_vnode for help."
1721 return
1722 ShowVnode(cmd_args)
1723
1724 @lldb_command('showworkqvnodes')
1725 def ShowWorkqVnodes(cmd_args=None):
1726 """ Print the vnode worker list
1727 Usage: showworkqvnodes <struct mount *>
1728 """
1729 if not cmd_args:
1730 print "Please provide valid mount argument. Type help showworkqvnodes for help."
1731 return
1732
1733 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
1734 vp = Cast(mp.mnt_workerqueue.tqh_first, 'vnode *')
1735 print GetVnodeSummary.header
1736 while int(vp) != 0:
1737 print GetVnodeSummary(vp)
1738 vp = vp.v_mntvnodes.tqe_next
1739
1740 @lldb_command('shownewvnodes')
1741 def ShowNewVnodes(cmd_args=None):
1742 """ Print the new vnode list
1743 Usage: shownewvnodes <struct mount *>
1744 """
1745 if not cmd_args:
1746 print "Please provide valid mount argument. Type help shownewvnodes for help."
1747 return
1748 mp = kern.GetValueFromAddress(cmd_args[0], 'mount *')
1749 vp = Cast(mp.mnt_newvnodes.tqh_first, 'vnode *')
1750 print GetVnodeSummary.header
1751 while int(vp) != 0:
1752 print GetVnodeSummary(vp)
1753 vp = vp.v_mntvnodes.tqe_next
1754
1755
1756 @lldb_command('showprocvnodes')
1757 def ShowProcVnodes(cmd_args=None):
1758 """ Routine to print out all the open fds which are vnodes in a process
1759 Usage: showprocvnodes <proc *>
1760 """
1761 if not cmd_args:
1762 print "Please provide valid proc argument. Type help showprocvnodes for help."
1763 return
1764 procptr = kern.GetValueFromAddress(cmd_args[0], 'proc *')
1765 fdptr = Cast(procptr.p_fd, 'filedesc *')
1766 if int(fdptr.fd_cdir) != 0:
1767 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Working Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_cdir))
1768 if int(fdptr.fd_rdir) != 0:
1769 print '{0: <25s}\n{1: <s}\n{2: <s}'.format('Current Root Directory:', GetVnodeSummary.header, GetVnodeSummary(fdptr.fd_rdir))
1770 count = 0
1771 print '\n' + '{0: <5s} {1: <7s}'.format('fd', 'flags') + GetVnodeSummary.header
1772 # Hack to get around <rdar://problem/12879494> llb fails to cast addresses to double pointers
1773 fpptr = Cast(fdptr.fd_ofiles, 'fileproc *')
1774 while count < fdptr.fd_nfiles:
1775 fpp = dereference(fpptr)
1776 fproc = Cast(fpp, 'fileproc *')
1777 if int(fproc) != 0:
1778 fglob = dereference(fproc).f_fglob
1779 flags = ""
1780 if (int(fglob) != 0) and (int(fglob.fg_ops.fo_type) == 1):
1781 if (fdptr.fd_ofileflags[count] & 1): flags += 'E'
1782 if (fdptr.fd_ofileflags[count] & 2): flags += 'F'
1783 if (fdptr.fd_ofileflags[count] & 4): flags += 'R'
1784 if (fdptr.fd_ofileflags[count] & 8): flags += 'C'
1785 print '{0: <5d} {1: <7s}'.format(count, flags) + GetVnodeSummary(Cast(fglob.fg_data, 'vnode *'))
1786 count += 1
1787 fpptr = kern.GetValueFromAddress(int(fpptr) + kern.ptrsize,'fileproc *')
1788
1789 @lldb_command('showallprocvnodes')
1790 def ShowAllProcVnodes(cmd_args=None):
1791 """ Routine to print out all the open fds which are vnodes
1792 """
1793
1794 procptr = Cast(kern.globals.allproc.lh_first, 'proc *')
1795 while procptr and int(procptr) != 0:
1796 print '{:<s}'.format("=" * 106)
1797 print GetProcInfo(procptr)
1798 ShowProcVnodes([int(procptr)])
1799 procptr = procptr.p_list.le_next
1800
1801 @xnudebug_test('test_vnode')
1802 def TestShowAllVnodes(kernel_target, config, lldb_obj, isConnected ):
1803 """ Test the functionality of vnode related commands
1804 returns
1805 - False on failure
1806 - True on success
1807 """
1808 if not isConnected:
1809 print "Target is not connected. Cannot test memstats"
1810 return False
1811 res = lldb.SBCommandReturnObject()
1812 lldb_obj.debugger.GetCommandInterpreter().HandleCommand("showallvnodes", res)
1813 result = res.GetOutput()
1814 if len(result.split("\n")) > 2 and result.find('VREG') != -1 and len(result.splitlines()[2].split()) > 5:
1815 return True
1816 else:
1817 return False
1818
1819 # Macro: showallmtx
1820 @lldb_type_summary(['_lck_grp_ *'])
1821 def GetMutexEntry(mtxg):
1822 """ Summarize a mutex group entry with important information.
1823 params:
1824 mtxg: value - obj representing a mutex group in kernel
1825 returns:
1826 out_string - summary of the mutex group
1827 """
1828 out_string = ""
1829
1830 if kern.ptrsize == 8:
1831 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1832 else:
1833 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1834
1835 if mtxg.lck_grp_mtxcnt:
1836 out_string += format_string.format(mtxg, mtxg.lck_grp_mtxcnt,mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_util_cnt,
1837 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_miss_cnt,
1838 mtxg.lck_grp_stat.lck_grp_mtx_stat.lck_grp_mtx_wait_cnt, mtxg.lck_grp_name)
1839 return out_string
1840
1841 @lldb_command('showallmtx')
1842 def ShowAllMtx(cmd_args=None):
1843 """ Routine to print a summary listing of all mutexes
1844 """
1845
1846 if kern.ptrsize == 8:
1847 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1848 else:
1849 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
1850
1851 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
1852
1853 mtxgrp_queue_head = kern.globals.lck_grp_queue
1854 mtxgrp_ptr_type = GetType('_lck_grp_ *')
1855
1856 for mtxgrp_ptr in IterateQueue(mtxgrp_queue_head, mtxgrp_ptr_type, "lck_grp_link"):
1857 print GetMutexEntry(mtxgrp_ptr)
1858 return
1859 # EndMacro: showallmtx
1860
1861 # Macro: showallrwlck
1862 @lldb_type_summary(['_lck_grp_ *'])
1863 def GetRWLEntry(rwlg):
1864 """ Summarize a reader writer lock group with important information.
1865 params:
1866 rwlg: value - obj representing a reader writer lock group in kernel
1867 returns:
1868 out_string - summary of the reader writer lock group
1869 """
1870 out_string = ""
1871
1872 if kern.ptrsize == 8:
1873 format_string = '{0:#018x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1874 else:
1875 format_string = '{0:#010x} {1:10d} {2:10d} {3:10d} {4:10d} {5: <30s} '
1876
1877 if rwlg.lck_grp_rwcnt:
1878 out_string += format_string.format(rwlg, rwlg.lck_grp_rwcnt,rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_util_cnt,
1879 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_miss_cnt,
1880 rwlg.lck_grp_stat.lck_grp_rw_stat.lck_grp_rw_wait_cnt, rwlg.lck_grp_name)
1881 return out_string
1882
1883 #Macro: showlock
1884 @lldb_type_summary(['lck_mtx_t *'])
1885 @header("===== Mutex Lock Summary =====")
1886 def GetMutexLockSummary(mtx):
1887 """ Summarize mutex lock with important information.
1888 params:
1889 mtx: value - obj representing a mutex lock in kernel
1890 returns:
1891 out_str - summary of the mutex lock
1892 """
1893 if not mtx:
1894 return "Invalid lock value: 0x0"
1895
1896 if kern.arch == "x86_64":
1897 out_str = "Lock Type\t\t: MUTEX\n"
1898 mtxd = mtx.lck_mtx_sw.lck_mtxd
1899 out_str += "Owner Thread\t\t: {:#x}\n".format(mtxd.lck_mtxd_owner)
1900 cmd_str = "p/d ((lck_mtx_t*){:#x})->lck_mtx_sw.lck_mtxd.".format(mtx)
1901 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_waiters")
1902 out_str += "Number of Waiters\t: {:s}\n".format(cmd_out.split()[-1])
1903 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_ilocked")
1904 out_str += "ILocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1905 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_mlocked")
1906 out_str += "MLocked\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1907 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_promoted")
1908 out_str += "Promoted\t\t: {:s}\n".format(cmd_out.split()[-1])
1909 cmd_out = lldb_run_command(cmd_str + "lck_mtxd_spin")
1910 out_str += "Spin\t\t\t: {:s}\n".format(cmd_out.split()[-1])
1911 return out_str
1912
1913 out_str = "Lock Type\t\t: MUTEX\n"
1914 out_str += "Owner Thread\t\t: {:#x}\n".format(mtx.lck_mtx_hdr.lck_mtxd_data & ~0x3)
1915 out_str += "Number of Waiters\t: {:d}\n".format(mtx.lck_mtx_sw.lck_mtxd.lck_mtxd_waiters)
1916 out_str += "Flags\t\t\t: "
1917 if mtx.lck_mtx_hdr.lck_mtxd_data & 0x1:
1918 out_str += "[Interlock Locked] "
1919 if mtx.lck_mtx_hdr.lck_mtxd_data & 0x2:
1920 out_str += "[Wait Flag]"
1921 if (mtx.lck_mtx_hdr.lck_mtxd_data & 0x3) == 0:
1922 out_str += "None"
1923 return out_str
1924
1925 @lldb_type_summary(['lck_spin_t *'])
1926 @header("===== SpinLock Summary =====")
1927 def GetSpinLockSummary(spinlock):
1928 """ Summarize spinlock with important information.
1929 params:
1930 spinlock: value - obj representing a spinlock in kernel
1931 returns:
1932 out_str - summary of the spinlock
1933 """
1934 if not spinlock:
1935 return "Invalid lock value: 0x0"
1936
1937 out_str = "Lock Type\t\t: SPINLOCK\n"
1938 if kern.arch == "x86_64":
1939 out_str += "Interlock\t\t: {:#x}\n".format(spinlock.interlock)
1940 return out_str
1941
1942 out_str += "Owner Thread\t\t: {:#x}\n".format(spinlock.lck_spin_data & ~0x3)
1943 out_str += "Flags\t\t\t: "
1944 if spinlock.lck_spin_data & 0x1:
1945 out_str += "[Interlock Locked] "
1946 if spinlock.lck_spin_data & 0x2:
1947 out_str += "[Wait Flag]"
1948 if (spinlock.lck_spin_data & 0x3) == 0:
1949 out_str += "None"
1950 return out_str
1951
1952 @lldb_command('showlock', 'MS')
1953 def ShowLock(cmd_args=None, cmd_options={}):
1954 """ Show info about a lock - its state and owner thread details
1955 Usage: showlock <address of a lock>
1956 -M : to consider <addr> as lck_mtx_t
1957 -S : to consider <addr> as lck_spin_t
1958 """
1959 if not cmd_args:
1960 raise ArgumentError("Please specify the address of the lock whose info you want to view.")
1961 return
1962
1963 summary_str = ""
1964 lock = kern.GetValueFromAddress(cmd_args[0], 'uintptr_t*')
1965
1966 if kern.arch == "x86_64" and lock:
1967 if "-M" in cmd_options:
1968 lock_mtx = Cast(lock, 'lck_mtx_t *')
1969 summary_str = GetMutexLockSummary(lock_mtx)
1970 elif "-S" in cmd_options:
1971 lock_spin = Cast(lock, 'lck_spin_t *')
1972 summary_str = GetSpinLockSummary(lock_spin)
1973 else:
1974 summary_str = "Please specify supported lock option(-M/-S)"
1975
1976 print summary_str
1977 return
1978
1979 if lock:
1980 lock_mtx = Cast(lock, 'lck_mtx_t*')
1981 if lock_mtx.lck_mtx_type == 0x22:
1982 summary_str = GetMutexLockSummary(lock_mtx)
1983
1984 lock_spin = Cast(lock, 'lck_spin_t*')
1985 if lock_spin.lck_spin_type == 0x11:
1986 summary_str = GetSpinLockSummary(lock_spin)
1987
1988 if summary_str == "":
1989 summary_str = "Lock Type\t\t: INVALID LOCK"
1990 print summary_str
1991
1992 #EndMacro: showlock
1993
1994 @lldb_command('showallrwlck')
1995 def ShowAllRWLck(cmd_args=None):
1996 """ Routine to print a summary listing of all read/writer locks
1997 """
1998 if kern.ptrsize == 8:
1999 hdr_format = '{:<18s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2000 else:
2001 hdr_format = '{:<10s} {:>10s} {:>10s} {:>10s} {:>10s} {:<30s} '
2002
2003 print hdr_format.format('LCK GROUP', 'CNT', 'UTIL', 'MISS', 'WAIT', 'NAME')
2004
2005 rwlgrp_queue_head = kern.globals.lck_grp_queue
2006 rwlgrp_ptr_type = GetType('_lck_grp_ *')
2007 for rwlgrp_ptr in IterateQueue(rwlgrp_queue_head, rwlgrp_ptr_type, "lck_grp_link"):
2008 print GetRWLEntry(rwlgrp_ptr)
2009 return
2010 # EndMacro: showallrwlck
2011
2012 #Macro: showbootermemorymap
2013 @lldb_command('showbootermemorymap')
2014 def ShowBooterMemoryMap(cmd_args=None):
2015 """ Prints out the phys memory map from kernelBootArgs
2016 Supported only on x86_64
2017 """
2018 if kern.arch == 'x86_64':
2019 voffset = unsigned(0xFFFFFF8000000000)
2020 else:
2021 print "showbootermemorymap not supported on this architecture"
2022 return
2023
2024 out_string = ""
2025
2026 # Memory type map
2027 memtype_dict = {
2028 0: 'Reserved',
2029 1: 'LoaderCode',
2030 2: 'LoaderData',
2031 3: 'BS_code',
2032 4: 'BS_data',
2033 5: 'RT_code',
2034 6: 'RT_data',
2035 7: 'Convention',
2036 8: 'Unusable',
2037 9: 'ACPI_recl',
2038 10: 'ACPI_NVS',
2039 11: 'MemMapIO',
2040 12: 'MemPortIO',
2041 13: 'PAL_code'
2042 }
2043
2044 boot_args = kern.globals.kernelBootArgs
2045 msize = boot_args.MemoryMapDescriptorSize
2046 mcount = (boot_args.MemoryMapSize) / unsigned(msize)
2047
2048 out_string += "{0: <12s} {1: <19s} {2: <19s} {3: <19s} {4: <10s}\n".format("Type", "Physical Start", "Number of Pages", "Virtual Start", "Attributes")
2049
2050 i = 0
2051 while i < mcount:
2052 mptr = kern.GetValueFromAddress(unsigned(boot_args.MemoryMap) + voffset + unsigned(i*msize), 'EfiMemoryRange *')
2053 mtype = unsigned(mptr.Type)
2054 if mtype in memtype_dict:
2055 out_string += "{0: <12s}".format(memtype_dict[mtype])
2056 else:
2057 out_string += "{0: <12s}".format("UNKNOWN")
2058
2059 if mptr.VirtualStart == 0:
2060 out_string += "{0: #019x} {1: #019x} {2: <19s} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, ' '*19, mptr.Attribute)
2061 else:
2062 out_string += "{0: #019x} {1: #019x} {2: #019x} {3: #019x}\n".format(mptr.PhysicalStart, mptr.NumberOfPages, mptr.VirtualStart, mptr.Attribute)
2063 i = i + 1
2064
2065 print out_string
2066 #EndMacro: showbootermemorymap
2067
2068 @lldb_command('show_all_purgeable_objects')
2069 def ShowAllPurgeableVmObjects(cmd_args=None):
2070 """ Routine to print a summary listing of all the purgeable vm objects
2071 """
2072 print "\n-------------------- VOLATILE OBJECTS --------------------\n"
2073 ShowAllPurgeableVolatileVmObjects()
2074 print "\n-------------------- NON-VOLATILE OBJECTS --------------------\n"
2075 ShowAllPurgeableNonVolatileVmObjects()
2076
2077 @lldb_command('show_all_purgeable_nonvolatile_objects')
2078 def ShowAllPurgeableNonVolatileVmObjects(cmd_args=None):
2079 """ Routine to print a summary listing of all the vm objects in
2080 the purgeable_nonvolatile_queue
2081 """
2082
2083 nonvolatile_total = lambda:None
2084 nonvolatile_total.objects = 0
2085 nonvolatile_total.vsize = 0
2086 nonvolatile_total.rsize = 0
2087 nonvolatile_total.wsize = 0
2088 nonvolatile_total.csize = 0
2089 nonvolatile_total.disowned_objects = 0
2090 nonvolatile_total.disowned_vsize = 0
2091 nonvolatile_total.disowned_rsize = 0
2092 nonvolatile_total.disowned_wsize = 0
2093 nonvolatile_total.disowned_csize = 0
2094
2095 queue_len = kern.globals.purgeable_nonvolatile_count
2096 queue_head = kern.globals.purgeable_nonvolatile_queue
2097
2098 print 'purgeable_nonvolatile_queue:{:#018x} purgeable_volatile_count:{:d}\n'.format(kern.GetLoadAddressForSymbol('purgeable_nonvolatile_queue'),queue_len)
2099 print 'N:non-volatile V:volatile E:empty D:deny\n'
2100
2101 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")
2102 idx = 0
2103 for object in IterateQueue(queue_head, 'struct vm_object *', 'objq'):
2104 idx += 1
2105 ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total)
2106 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)
2107 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)
2108
2109
2110 def ShowPurgeableNonVolatileVmObject(object, idx, queue_len, nonvolatile_total):
2111 """ Routine to print out a summary a VM object in purgeable_nonvolatile_queue
2112 params:
2113 object - core.value : a object of type 'struct vm_object *'
2114 returns:
2115 None
2116 """
2117 if object.purgable == 0:
2118 purgable = "N"
2119 elif object.purgable == 1:
2120 purgable = "V"
2121 elif object.purgable == 2:
2122 purgable = "E"
2123 elif object.purgable == 3:
2124 purgable = "D"
2125 else:
2126 purgable = "?"
2127 if object.pager == 0:
2128 compressed_count = 0
2129 else:
2130 compressor_pager = Cast(object.pager, 'compressor_pager *')
2131 compressed_count = compressor_pager.cpgr_num_slots_occupied
2132
2133 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/kern.globals.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))
2134
2135 nonvolatile_total.objects += 1
2136 nonvolatile_total.vsize += object.vo_un1.vou_size/kern.globals.page_size
2137 nonvolatile_total.rsize += object.resident_page_count
2138 nonvolatile_total.wsize += object.wired_page_count
2139 nonvolatile_total.csize += compressed_count
2140 if object.vo_un2.vou_purgeable_owner == 0:
2141 nonvolatile_total.disowned_objects += 1
2142 nonvolatile_total.disowned_vsize += object.vo_un1.vou_size/kern.globals.page_size
2143 nonvolatile_total.disowned_rsize += object.resident_page_count
2144 nonvolatile_total.disowned_wsize += object.wired_page_count
2145 nonvolatile_total.disowned_csize += compressed_count
2146
2147
2148 @lldb_command('show_all_purgeable_volatile_objects')
2149 def ShowAllPurgeableVolatileVmObjects(cmd_args=None):
2150 """ Routine to print a summary listing of all the vm objects in
2151 the purgeable queues
2152 """
2153 volatile_total = lambda:None
2154 volatile_total.objects = 0
2155 volatile_total.vsize = 0
2156 volatile_total.rsize = 0
2157 volatile_total.wsize = 0
2158 volatile_total.csize = 0
2159 volatile_total.disowned_objects = 0
2160 volatile_total.disowned_vsize = 0
2161 volatile_total.disowned_rsize = 0
2162 volatile_total.disowned_wsize = 0
2163 volatile_total.disowned_csize = 0
2164
2165 purgeable_queues = kern.globals.purgeable_queues
2166 print "---------- OBSOLETE\n"
2167 ShowPurgeableQueue(purgeable_queues[0], volatile_total)
2168 print "\n\n---------- FIFO\n"
2169 ShowPurgeableQueue(purgeable_queues[1], volatile_total)
2170 print "\n\n---------- LIFO\n"
2171 ShowPurgeableQueue(purgeable_queues[2], volatile_total)
2172
2173 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)
2174 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)
2175 purgeable_count = kern.globals.vm_page_purgeable_count
2176 purgeable_wired_count = kern.globals.vm_page_purgeable_wired_count
2177 if purgeable_count != volatile_total.rsize or purgeable_wired_count != volatile_total.wsize:
2178 mismatch = "<--------- MISMATCH\n"
2179 else:
2180 mismatch = ""
2181 print "vm_page_purgeable_count: resident:{:<10d} wired:{:<10d} {:s}\n".format(purgeable_count, purgeable_wired_count, mismatch)
2182
2183
2184 def ShowPurgeableQueue(qhead, volatile_total):
2185 print "----- GROUP 0\n"
2186 ShowPurgeableGroup(qhead.objq[0], volatile_total)
2187 print "----- GROUP 1\n"
2188 ShowPurgeableGroup(qhead.objq[1], volatile_total)
2189 print "----- GROUP 2\n"
2190 ShowPurgeableGroup(qhead.objq[2], volatile_total)
2191 print "----- GROUP 3\n"
2192 ShowPurgeableGroup(qhead.objq[3], volatile_total)
2193 print "----- GROUP 4\n"
2194 ShowPurgeableGroup(qhead.objq[4], volatile_total)
2195 print "----- GROUP 5\n"
2196 ShowPurgeableGroup(qhead.objq[5], volatile_total)
2197 print "----- GROUP 6\n"
2198 ShowPurgeableGroup(qhead.objq[6], volatile_total)
2199 print "----- GROUP 7\n"
2200 ShowPurgeableGroup(qhead.objq[7], volatile_total)
2201
2202 def ShowPurgeableGroup(qhead, volatile_total):
2203 idx = 0
2204 for object in IterateQueue(qhead, 'struct vm_object *', 'objq'):
2205 if idx == 0:
2206 # 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","")
2207 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")
2208 idx += 1
2209 ShowPurgeableVolatileVmObject(object, idx, volatile_total)
2210
2211 def ShowPurgeableVolatileVmObject(object, idx, volatile_total):
2212 """ Routine to print out a summary a VM object in a purgeable queue
2213 params:
2214 object - core.value : a object of type 'struct vm_object *'
2215 returns:
2216 None
2217 """
2218 # if int(object.vo_un2.vou_purgeable_owner) != int(object.vo_purgeable_volatilizer):
2219 # diff=" !="
2220 # else:
2221 # diff=" "
2222 if object.purgable == 0:
2223 purgable = "N"
2224 elif object.purgable == 1:
2225 purgable = "V"
2226 elif object.purgable == 2:
2227 purgable = "E"
2228 elif object.purgable == 3:
2229 purgable = "D"
2230 else:
2231 purgable = "?"
2232 if object.pager == 0:
2233 compressed_count = 0
2234 else:
2235 compressor_pager = Cast(object.pager, 'compressor_pager *')
2236 compressed_count = compressor_pager.cpgr_num_slots_occupied
2237 # 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/kern.globals.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)
2238 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/kern.globals.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))
2239 volatile_total.objects += 1
2240 volatile_total.vsize += object.vo_un1.vou_size/kern.globals.page_size
2241 volatile_total.rsize += object.resident_page_count
2242 volatile_total.wsize += object.wired_page_count
2243 volatile_total.csize += compressed_count
2244 if object.vo_un2.vou_purgeable_owner == 0:
2245 volatile_total.disowned_objects += 1
2246 volatile_total.disowned_vsize += object.vo_un1.vou_size/kern.globals.page_size
2247 volatile_total.disowned_rsize += object.resident_page_count
2248 volatile_total.disowned_wsize += object.wired_page_count
2249 volatile_total.disowned_csize += compressed_count
2250
2251
2252 def GetCompressedPagesForObject(obj):
2253 """Stuff
2254 """
2255 pager = Cast(obj.pager, 'compressor_pager_t')
2256 return pager.cpgr_num_slots_occupied
2257 # if pager.cpgr_num_slots > 128:
2258 # slots_arr = pager.cpgr_slots.cpgr_islots
2259 # num_indirect_slot_ptr = (pager.cpgr_num_slots + 127) / 128
2260 # index = 0
2261 # compressor_slot = 0
2262 # compressed_pages = 0
2263 # while index < num_indirect_slot_ptr:
2264 # compressor_slot = 0
2265 # if slots_arr[index]:
2266 # while compressor_slot < 128:
2267 # if slots_arr[index][compressor_slot]:
2268 # compressed_pages += 1
2269 # compressor_slot += 1
2270 # index += 1
2271 # else:
2272 # slots_arr = pager.cpgr_slots.cpgr_dslots
2273 # compressor_slot = 0
2274 # compressed_pages = 0
2275 # while compressor_slot < pager.cpgr_num_slots:
2276 # if slots_arr[compressor_slot]:
2277 # compressed_pages += 1
2278 # compressor_slot += 1
2279 # return compressed_pages
2280
2281 @lldb_command('showallvme', "-PS")
2282 def ShowAllVME(cmd_args=None, cmd_options={}):
2283 """ Routine to print a summary listing of all the vm map entries
2284 Go Through each task in system and show the vm info
2285 """
2286 show_pager_info = False
2287 show_all_shadows = False
2288 if "-P" in cmd_options:
2289 show_pager_info = True
2290 if "-S" in cmd_options:
2291 show_all_shadows = True
2292 for task in kern.tasks:
2293 ShowTaskVMEntries(task, show_pager_info, show_all_shadows)
2294
2295 def ShowTaskVMEntries(task, show_pager_info, show_all_shadows):
2296 """ Routine to print out a summary listing of all the entries in a vm_map
2297 params:
2298 task - core.value : a object of type 'task *'
2299 returns:
2300 None
2301 """
2302 print "vm_map entries for task " + hex(task)
2303 print GetTaskSummary.header
2304 print GetTaskSummary(task)
2305 if not task.map:
2306 print "Task {0: <#020x} has map = 0x0"
2307 return None
2308 showmapvme(task.map, show_pager_info, show_all_shadows)
2309
2310 @lldb_command("showmapvme", "PS")
2311 def ShowMapVME(cmd_args=None, cmd_options={}):
2312 """Routine to print out info about the specified vm_map and its vm entries
2313 usage: showmapvme <vm_map>
2314 """
2315 if cmd_args == None or len(cmd_args) < 1:
2316 print "Invalid argument.", ShowMap.__doc__
2317 return
2318 show_pager_info = False
2319 show_all_shadows = False
2320 if "-P" in cmd_options:
2321 show_pager_info = True
2322 if "-S" in cmd_options:
2323 show_all_shadows = True
2324 map = kern.GetValueFromAddress(cmd_args[0], 'vm_map_t')
2325 showmapvme(map, show_pager_info, show_all_shadows)
2326
2327 def showmapvme(map, show_pager_info, show_all_shadows):
2328 vnode_pager_ops = kern.globals.vnode_pager_ops
2329 vnode_pager_ops_addr = unsigned(addressof(vnode_pager_ops))
2330 rsize = 0
2331 if map.pmap != 0:
2332 rsize = int(map.pmap.stats.resident_count)
2333 print "{:<18s} {:<18s} {:<18s} {:>10s} {:>10s} {:>18s}:{:<18s}".format("vm_map","pmap","size","#ents","rsize","start","end")
2334 print "{:#018x} {:#018x} {:#018x} {:>10d} {:>10d} {:#018x}:{:#018x}".format(map,map.pmap,(map.size/4096),map.hdr.nentries,rsize,map.hdr.links.start,map.hdr.links.end)
2335 vme_list_head = map.hdr.links
2336 vme_ptr_type = GetType('vm_map_entry *')
2337 print "{:<18s} {:>18s}:{:<18s} {:>10s} {:>3s} {:<10s} {:<18s} {:<18s}".format("entry","start","end","#pgs","tag","prot&flags","object","offset")
2338 last_end = map.hdr.links.start
2339 for vme in IterateQueue(vme_list_head, vme_ptr_type, "links"):
2340 if vme.links.start != last_end:
2341 print "{:18s} {:#018x}:{:#018x} {:>10d}".format("------------------",last_end,vme.links.start,(vme.links.start-last_end)/4096)
2342 last_end = vme.links.end
2343 vme_flags = ""
2344 if vme.is_sub_map:
2345 vme_flags += "s"
2346 print "{:#018x} {:#018x}:{:#018x} {:>10d} {:>3d} {:1d}{:1d}{:<8s} {:#018x} {:<#18x}".format(vme,vme.links.start,vme.links.end,(vme.links.end-vme.links.start)/4096,vme.alias,vme.protection,vme.max_protection,vme_flags,vme.object.vm_object,vme.offset)
2347 if show_pager_info and vme.is_sub_map == 0 and vme.object.vm_object != 0:
2348 object = vme.object.vm_object
2349 else:
2350 object = 0
2351 depth = 0
2352 offset = unsigned(vme.offset)
2353 size = vme.links.end - vme.links.start
2354 while object != 0:
2355 depth += 1
2356 if show_all_shadows == False and depth != 1 and object.shadow != 0:
2357 offset += unsigned(object.vo_un2.vou_shadow_offset)
2358 object = object.shadow
2359 continue
2360 if object.copy_strategy == 0:
2361 copy_strategy="N"
2362 elif object.copy_strategy == 2:
2363 copy_strategy="D"
2364 elif object.copy_strategy == 4:
2365 copy_strategy="S"
2366 else:
2367 copy_strategy=str(object.copy_strategy)
2368 if object.internal:
2369 internal = "internal"
2370 else:
2371 internal = "external"
2372 pager_string = ""
2373 if show_pager_info and object.pager != 0:
2374 if object.internal:
2375 pager_string = "-> compressed:{:d}".format(GetCompressedPagesForObject(object))
2376 else:
2377 vnode_pager = Cast(object.pager,'vnode_pager *')
2378 if unsigned(vnode_pager.pager_ops) == vnode_pager_ops_addr:
2379 pager_string = "-> " + GetVnodePath(vnode_pager.vnode_handle)
2380 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)/4096,object.resident_page_count,object.wired_page_count,pager_string)
2381 # 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/4096,object.wired_page_count,object.resident_page_count,object.reusable_page_count)
2382 offset += unsigned(object.vo_un2.vou_shadow_offset)
2383 object = object.shadow
2384 return None
2385
2386 def FindVMEntriesForVnode(task, vn):
2387 """ returns an array of vme that have the vnode set to defined vnode
2388 each entry in array is of format (vme, start_addr, end_address, protection)
2389 """
2390 retval = []
2391 vmmap = task.map
2392 pmap = vmmap.pmap
2393 pager_ops_addr = unsigned(addressof(kern.globals.vnode_pager_ops))
2394 debuglog("pager_ops_addr %s" % hex(pager_ops_addr))
2395
2396 if unsigned(pmap) == 0:
2397 return retval
2398 vme_list_head = vmmap.hdr.links
2399 vme_ptr_type = gettype('vm_map_entry *')
2400 for vme in IterateQueue(vme_list_head, vme_ptr_type, 'links'):
2401 #print vme
2402 if unsigned(vme.is_sub_map) == 0 and unsigned(vme.object.vm_object) != 0:
2403 obj = vme.object.vm_object
2404 else:
2405 continue
2406
2407 while obj != 0:
2408 if obj.pager != 0:
2409 if obj.internal:
2410 pass
2411 else:
2412 vn_pager = Cast(obj.pager, 'vnode_pager *')
2413 if unsigned(vn_pager.pager_ops) == pager_ops_addr and unsigned(vn_pager.vnode_handle) == unsigned(vn):
2414 retval.append((vme, unsigned(vme.links.start), unsigned(vme.links.end), unsigned(vme.protection)))
2415 obj = obj.shadow
2416 return retval
2417
2418 @lldb_command('showtaskloadinfo')
2419 def ShowTaskLoadInfo(cmd_args=None, cmd_options={}):
2420 """ Print the load address and uuid for the process
2421 Usage: (lldb)showtaskloadinfo <task_t>
2422 """
2423 if not cmd_args:
2424 raise ArgumentError("Insufficient arguments")
2425 t = kern.GetValueFromAddress(cmd_args[0], 'struct task *')
2426 print_format = "0x{0:x} - 0x{1:x} {2: <50s} (??? - ???) <{3: <36s}> {4: <50s}"
2427 p = Cast(t.bsd_info, 'struct proc *')
2428 uuid = p.p_uuid
2429 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)
2430 filepath = GetVnodePath(p.p_textvp)
2431 libname = filepath.split('/')[-1]
2432 #print "uuid: %s file: %s" % (uuid_out_string, filepath)
2433 mappings = FindVMEntriesForVnode(t, p.p_textvp)
2434 load_addr = 0
2435 end_addr = 0
2436 for m in mappings:
2437 if m[3] == 5:
2438 load_addr = m[1]
2439 end_addr = m[2]
2440 #print "Load address: %s" % hex(m[1])
2441 print print_format.format(load_addr, end_addr, libname, uuid_out_string, filepath)
2442 return None