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