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