[docs] Fix build-docs.sh
[llvm-project.git] / lldb / examples / darwin / heap_find / heap.py
blob2021b9ebc8e230b060b15a4d9888ddc4f086d4ee
1 #!/usr/bin/env python3
3 #----------------------------------------------------------------------
4 # This module is designed to live inside the "lldb" python package
5 # in the "lldb.macosx" package. To use this in the embedded python
6 # interpreter using "lldb" just import it:
8 # (lldb) script import lldb.macosx.heap
9 #----------------------------------------------------------------------
11 import lldb
12 import optparse
13 import os
14 import os.path
15 import re
16 import shlex
17 import string
18 import sys
19 import tempfile
20 import lldb.utils.symbolication
22 g_libheap_dylib_dir = None
23 g_libheap_dylib_dict = dict()
26 def get_iterate_memory_expr(
27 options,
28 process,
29 user_init_code,
30 user_return_code):
31 expr = '''
32 typedef unsigned natural_t;
33 typedef uintptr_t vm_size_t;
34 typedef uintptr_t vm_address_t;
35 typedef natural_t task_t;
36 typedef int kern_return_t;
37 #define KERN_SUCCESS 0
38 typedef void (*range_callback_t)(task_t, void *, unsigned, uintptr_t, uintptr_t);
39 '''
40 if options.search_vm_regions:
41 expr += '''
42 typedef int vm_prot_t;
43 typedef unsigned int vm_inherit_t;
44 typedef unsigned long long memory_object_offset_t;
45 typedef unsigned int boolean_t;
46 typedef int vm_behavior_t;
47 typedef uint32_t vm32_object_id_t;
48 typedef natural_t mach_msg_type_number_t;
49 typedef uint64_t mach_vm_address_t;
50 typedef uint64_t mach_vm_offset_t;
51 typedef uint64_t mach_vm_size_t;
52 typedef uint64_t vm_map_offset_t;
53 typedef uint64_t vm_map_address_t;
54 typedef uint64_t vm_map_size_t;
55 #define VM_PROT_NONE ((vm_prot_t) 0x00)
56 #define VM_PROT_READ ((vm_prot_t) 0x01)
57 #define VM_PROT_WRITE ((vm_prot_t) 0x02)
58 #define VM_PROT_EXECUTE ((vm_prot_t) 0x04)
59 typedef struct vm_region_submap_short_info_data_64_t {
60 vm_prot_t protection;
61 vm_prot_t max_protection;
62 vm_inherit_t inheritance;
63 memory_object_offset_t offset; // offset into object/map
64 unsigned int user_tag; // user tag on map entry
65 unsigned int ref_count; // obj/map mappers, etc
66 unsigned short shadow_depth; // only for obj
67 unsigned char external_pager; // only for obj
68 unsigned char share_mode; // see enumeration
69 boolean_t is_submap; // submap vs obj
70 vm_behavior_t behavior; // access behavior hint
71 vm32_object_id_t object_id; // obj/map name, not a handle
72 unsigned short user_wired_count;
73 } vm_region_submap_short_info_data_64_t;
74 #define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))'''
75 if user_init_code:
76 expr += user_init_code
77 expr += '''
78 task_t task = (task_t)mach_task_self();
79 mach_vm_address_t vm_region_base_addr;
80 mach_vm_size_t vm_region_size;
81 natural_t vm_region_depth;
82 vm_region_submap_short_info_data_64_t vm_region_info;
83 kern_return_t err;
84 for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size)
86 mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
87 err = (kern_return_t)mach_vm_region_recurse (task,
88 &vm_region_base_addr,
89 &vm_region_size,
90 &vm_region_depth,
91 &vm_region_info,
92 &vm_region_info_size);
93 if (err)
94 break;
95 // Check all read + write regions. This will cover the thread stacks
96 // and any regions of memory like __DATA segments, that might contain
97 // data we are looking for
98 if (vm_region_info.protection & VM_PROT_WRITE &&
99 vm_region_info.protection & VM_PROT_READ)
101 baton.callback (task,
102 &baton,
104 vm_region_base_addr,
105 vm_region_size);
107 }'''
108 else:
109 if options.search_stack:
110 expr += get_thread_stack_ranges_struct(process)
111 if options.search_segments:
112 expr += get_sections_ranges_struct(process)
113 if user_init_code:
114 expr += user_init_code
115 if options.search_heap:
116 expr += '''
117 #define MALLOC_PTR_IN_USE_RANGE_TYPE 1
118 typedef struct vm_range_t {
119 vm_address_t address;
120 vm_size_t size;
121 } vm_range_t;
122 typedef kern_return_t (*memory_reader_t)(task_t, vm_address_t, vm_size_t, void **);
123 typedef void (*vm_range_recorder_t)(task_t, void *, unsigned, vm_range_t *, unsigned);
124 typedef struct malloc_introspection_t {
125 kern_return_t (*enumerator)(task_t task, void *, unsigned type_mask, vm_address_t zone_address, memory_reader_t reader, vm_range_recorder_t recorder); /* enumerates all the malloc pointers in use */
126 } malloc_introspection_t;
127 typedef struct malloc_zone_t {
128 void *reserved1[12];
129 struct malloc_introspection_t *introspect;
130 } malloc_zone_t;
131 kern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *);
132 memory_reader_t task_peek = [](task_t, vm_address_t remote_address, vm_size_t, void **local_memory) -> kern_return_t {
133 *local_memory = (void*) remote_address;
134 return KERN_SUCCESS;
136 vm_address_t *zones = 0;
137 unsigned int num_zones = 0;task_t task = 0;
138 kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones);
139 if (KERN_SUCCESS == err)
141 for (unsigned int i=0; i<num_zones; ++i)
143 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i];
144 if (zone && zone->introspect)
145 zone->introspect->enumerator (task,
146 &baton,
147 MALLOC_PTR_IN_USE_RANGE_TYPE,
148 (vm_address_t)zone,
149 task_peek,
150 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void
152 range_callback_t callback = ((callback_baton_t *)baton)->callback;
153 for (unsigned i=0; i<size; ++i)
155 callback (task, baton, type, ranges[i].address, ranges[i].size);
159 }'''
161 if options.search_stack:
162 expr += '''
163 #ifdef NUM_STACKS
164 // Call the callback for the thread stack ranges
165 for (uint32_t i=0; i<NUM_STACKS; ++i) {
166 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size);
167 if (STACK_RED_ZONE_SIZE > 0) {
168 range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE);
171 #endif'''
173 if options.search_segments:
174 expr += '''
175 #ifdef NUM_SEGMENTS
176 // Call the callback for all segments
177 for (uint32_t i=0; i<NUM_SEGMENTS; ++i)
178 range_callback(task, &baton, 32, segments[i].base, segments[i].size);
179 #endif'''
181 if user_return_code:
182 expr += "\n%s" % (user_return_code,)
184 return expr
187 def get_member_types_for_offset(value_type, offset, member_list):
188 member = value_type.GetFieldAtIndex(0)
189 search_bases = False
190 if member:
191 if member.GetOffsetInBytes() <= offset:
192 for field_idx in range(value_type.GetNumberOfFields()):
193 member = value_type.GetFieldAtIndex(field_idx)
194 member_byte_offset = member.GetOffsetInBytes()
195 member_end_byte_offset = member_byte_offset + member.type.size
196 if member_byte_offset <= offset and offset < member_end_byte_offset:
197 member_list.append(member)
198 get_member_types_for_offset(
199 member.type, offset - member_byte_offset, member_list)
200 return
201 else:
202 search_bases = True
203 else:
204 search_bases = True
205 if search_bases:
206 for field_idx in range(value_type.GetNumberOfDirectBaseClasses()):
207 member = value_type.GetDirectBaseClassAtIndex(field_idx)
208 member_byte_offset = member.GetOffsetInBytes()
209 member_end_byte_offset = member_byte_offset + member.type.size
210 if member_byte_offset <= offset and offset < member_end_byte_offset:
211 member_list.append(member)
212 get_member_types_for_offset(
213 member.type, offset - member_byte_offset, member_list)
214 return
215 for field_idx in range(value_type.GetNumberOfVirtualBaseClasses()):
216 member = value_type.GetVirtualBaseClassAtIndex(field_idx)
217 member_byte_offset = member.GetOffsetInBytes()
218 member_end_byte_offset = member_byte_offset + member.type.size
219 if member_byte_offset <= offset and offset < member_end_byte_offset:
220 member_list.append(member)
221 get_member_types_for_offset(
222 member.type, offset - member_byte_offset, member_list)
223 return
226 def append_regex_callback(option, opt, value, parser):
227 try:
228 ivar_regex = re.compile(value)
229 parser.values.ivar_regex_exclusions.append(ivar_regex)
230 except:
231 print('error: an exception was thrown when compiling the ivar regular expression for "%s"' % value)
234 def add_common_options(parser):
235 parser.add_option(
236 '-v',
237 '--verbose',
238 action='store_true',
239 dest='verbose',
240 help='display verbose debug info',
241 default=False)
242 parser.add_option(
243 '-t',
244 '--type',
245 action='store_true',
246 dest='print_type',
247 help='print the full value of the type for each matching malloc block',
248 default=False)
249 parser.add_option(
250 '-o',
251 '--po',
252 action='store_true',
253 dest='print_object_description',
254 help='print the object descriptions for any matches',
255 default=False)
256 parser.add_option(
257 '-z',
258 '--size',
259 action='store_true',
260 dest='show_size',
261 help='print the allocation size in bytes',
262 default=False)
263 parser.add_option(
264 '-r',
265 '--range',
266 action='store_true',
267 dest='show_range',
268 help='print the allocation address range instead of just the allocation base address',
269 default=False)
270 parser.add_option(
271 '-m',
272 '--memory',
273 action='store_true',
274 dest='memory',
275 help='dump the memory for each matching block',
276 default=False)
277 parser.add_option(
278 '-f',
279 '--format',
280 type='string',
281 dest='format',
282 help='the format to use when dumping memory if --memory is specified',
283 default=None)
284 parser.add_option(
285 '-I',
286 '--omit-ivar-regex',
287 type='string',
288 action='callback',
289 callback=append_regex_callback,
290 dest='ivar_regex_exclusions',
291 default=[],
292 help='specify one or more regular expressions used to backlist any matches that are in ivars')
293 parser.add_option(
294 '-s',
295 '--stack',
296 action='store_true',
297 dest='stack',
298 help='gets the stack that allocated each malloc block if MallocStackLogging is enabled',
299 default=False)
300 parser.add_option(
301 '-S',
302 '--stack-history',
303 action='store_true',
304 dest='stack_history',
305 help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled',
306 default=False)
307 parser.add_option(
308 '-F',
309 '--max-frames',
310 type='int',
311 dest='max_frames',
312 help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)',
313 default=128)
314 parser.add_option(
315 '-H',
316 '--max-history',
317 type='int',
318 dest='max_history',
319 help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)',
320 default=16)
321 parser.add_option(
322 '-M',
323 '--max-matches',
324 type='int',
325 dest='max_matches',
326 help='the maximum number of matches to print',
327 default=32)
328 parser.add_option(
329 '-O',
330 '--offset',
331 type='int',
332 dest='offset',
333 help='the matching data must be at this offset',
334 default=-1)
335 parser.add_option(
336 '--ignore-stack',
337 action='store_false',
338 dest='search_stack',
339 help="Don't search the stack when enumerating memory",
340 default=True)
341 parser.add_option(
342 '--ignore-heap',
343 action='store_false',
344 dest='search_heap',
345 help="Don't search the heap allocations when enumerating memory",
346 default=True)
347 parser.add_option(
348 '--ignore-segments',
349 action='store_false',
350 dest='search_segments',
351 help="Don't search readable executable segments enumerating memory",
352 default=True)
353 parser.add_option(
354 '-V',
355 '--vm-regions',
356 action='store_true',
357 dest='search_vm_regions',
358 help='Check all VM regions instead of searching the heap, stack and segments',
359 default=False)
362 def type_flags_to_string(type_flags):
363 if type_flags == 0:
364 type_str = 'free'
365 elif type_flags & 2:
366 type_str = 'malloc'
367 elif type_flags & 4:
368 type_str = 'free'
369 elif type_flags & 1:
370 type_str = 'generic'
371 elif type_flags & 8:
372 type_str = 'stack'
373 elif type_flags & 16:
374 type_str = 'stack (red zone)'
375 elif type_flags & 32:
376 type_str = 'segment'
377 elif type_flags & 64:
378 type_str = 'vm_region'
379 else:
380 type_str = hex(type_flags)
381 return type_str
384 def find_variable_containing_address(verbose, frame, match_addr):
385 variables = frame.GetVariables(True, True, True, True)
386 matching_var = None
387 for var in variables:
388 var_addr = var.GetLoadAddress()
389 if var_addr != lldb.LLDB_INVALID_ADDRESS:
390 byte_size = var.GetType().GetByteSize()
391 if verbose:
392 print('frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name))
393 if var_addr == match_addr:
394 if verbose:
395 print('match')
396 return var
397 else:
398 if byte_size > 0 and var_addr <= match_addr and match_addr < (
399 var_addr + byte_size):
400 if verbose:
401 print('match')
402 return var
403 return None
406 def find_frame_for_stack_address(process, addr):
407 closest_delta = sys.maxsize
408 closest_frame = None
409 # print 'find_frame_for_stack_address(%#x)' % (addr)
410 for thread in process:
411 prev_sp = lldb.LLDB_INVALID_ADDRESS
412 for frame in thread:
413 cfa = frame.GetCFA()
414 # print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa)
415 if addr < cfa:
416 delta = cfa - addr
417 # print '%#x < %#x, delta = %i' % (addr, cfa, delta)
418 if delta < closest_delta:
419 # print 'closest'
420 closest_delta = delta
421 closest_frame = frame
422 # else:
423 # print 'delta >= closest_delta'
424 return closest_frame
427 def type_flags_to_description(
428 process,
429 type_flags,
430 ptr_addr,
431 ptr_size,
432 offset,
433 match_addr):
434 show_offset = False
435 if type_flags == 0 or type_flags & 4:
436 type_str = 'free(%#x)' % (ptr_addr,)
437 elif type_flags & 2 or type_flags & 1:
438 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr)
439 show_offset = True
440 elif type_flags & 8:
441 type_str = 'stack'
442 frame = find_frame_for_stack_address(process, match_addr)
443 if frame:
444 type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID(
445 ), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID())
446 variables = frame.GetVariables(True, True, True, True)
447 matching_var = None
448 for var in variables:
449 var_addr = var.GetLoadAddress()
450 if var_addr != lldb.LLDB_INVALID_ADDRESS:
451 # print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr,
452 # match_addr)
453 if var_addr == match_addr:
454 matching_var = var
455 break
456 else:
457 byte_size = var.GetType().GetByteSize()
458 if byte_size > 0 and var_addr <= match_addr and match_addr < (
459 var_addr + byte_size):
460 matching_var = var
461 break
462 if matching_var:
463 type_str += ' in variable at %#x:\n %s' % (
464 matching_var.GetLoadAddress(), matching_var)
465 elif type_flags & 16:
466 type_str = 'stack (red zone)'
467 elif type_flags & 32:
468 sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
469 type_str = 'segment [%#x - %#x), %s + %u, %s' % (
470 ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
471 elif type_flags & 64:
472 sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset)
473 type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (
474 ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr)
475 else:
476 type_str = '%#x' % (ptr_addr,)
477 show_offset = True
478 if show_offset and offset != 0:
479 type_str += ' + %-6u' % (offset,)
480 return type_str
483 def dump_stack_history_entry(options, result, stack_history_entry, idx):
484 address = int(stack_history_entry.address)
485 if address:
486 type_flags = int(stack_history_entry.type_flags)
487 symbolicator = lldb.utils.symbolication.Symbolicator()
488 symbolicator.target = lldb.debugger.GetSelectedTarget()
489 type_str = type_flags_to_string(type_flags)
490 result.AppendMessage(
491 'stack[%u]: addr = 0x%x, type=%s, frames:' %
492 (idx, address, type_str))
493 frame_idx = 0
494 idx = 0
495 pc = int(stack_history_entry.frames[idx])
496 while pc != 0:
497 if pc >= 0x1000:
498 frames = symbolicator.symbolicate(pc)
499 if frames:
500 for frame in frames:
501 result.AppendMessage(
502 ' [%u] %s' %
503 (frame_idx, frame))
504 frame_idx += 1
505 else:
506 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc))
507 frame_idx += 1
508 idx = idx + 1
509 pc = int(stack_history_entry.frames[idx])
510 else:
511 pc = 0
512 if idx >= options.max_frames:
513 result.AppendMessage(
514 'warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' %
515 (options.max_frames))
517 result.AppendMessage('')
520 def dump_stack_history_entries(options, result, addr, history):
521 # malloc_stack_entry *get_stack_history_for_address (const void * addr)
522 expr_prefix = '''
523 typedef int kern_return_t;
524 typedef struct $malloc_stack_entry {
525 uint64_t address;
526 uint64_t argument;
527 uint32_t type_flags;
528 uint32_t num_frames;
529 uint64_t frames[512];
530 kern_return_t err;
531 } $malloc_stack_entry;
533 single_expr = '''
534 #define MAX_FRAMES %u
535 typedef unsigned task_t;
536 $malloc_stack_entry stack;
537 stack.address = 0x%x;
538 stack.type_flags = 2;
539 stack.num_frames = 0;
540 stack.frames[0] = 0;
541 uint32_t max_stack_frames = MAX_FRAMES;
542 stack.err = (kern_return_t)__mach_stack_logging_get_frames (
543 (task_t)mach_task_self(),
544 stack.address,
545 &stack.frames[0],
546 max_stack_frames,
547 &stack.num_frames);
548 if (stack.num_frames < MAX_FRAMES)
549 stack.frames[stack.num_frames] = 0;
550 else
551 stack.frames[MAX_FRAMES-1] = 0;
552 stack''' % (options.max_frames, addr)
554 history_expr = '''
555 typedef int kern_return_t;
556 typedef unsigned task_t;
557 #define MAX_FRAMES %u
558 #define MAX_HISTORY %u
559 typedef struct mach_stack_logging_record_t {
560 uint32_t type_flags;
561 uint64_t stack_identifier;
562 uint64_t argument;
563 uint64_t address;
564 } mach_stack_logging_record_t;
565 typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *);
566 typedef struct malloc_stack_entry {
567 uint64_t address;
568 uint64_t argument;
569 uint32_t type_flags;
570 uint32_t num_frames;
571 uint64_t frames[MAX_FRAMES];
572 kern_return_t frames_err;
573 } malloc_stack_entry;
574 typedef struct $malloc_stack_history {
575 task_t task;
576 unsigned idx;
577 malloc_stack_entry entries[MAX_HISTORY];
578 } $malloc_stack_history;
579 $malloc_stack_history lldb_info = { (task_t)mach_task_self(), 0 };
580 uint32_t max_stack_frames = MAX_FRAMES;
581 enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void {
582 $malloc_stack_history *lldb_info = ($malloc_stack_history *)baton;
583 if (lldb_info->idx < MAX_HISTORY) {
584 malloc_stack_entry *stack_entry = &(lldb_info->entries[lldb_info->idx]);
585 stack_entry->address = stack_record.address;
586 stack_entry->type_flags = stack_record.type_flags;
587 stack_entry->argument = stack_record.argument;
588 stack_entry->num_frames = 0;
589 stack_entry->frames[0] = 0;
590 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack (
591 lldb_info->task,
592 stack_record.stack_identifier,
593 stack_entry->frames,
594 (uint32_t)MAX_FRAMES,
595 &stack_entry->num_frames);
596 // Terminate the frames with zero if there is room
597 if (stack_entry->num_frames < MAX_FRAMES)
598 stack_entry->frames[stack_entry->num_frames] = 0;
600 ++lldb_info->idx;
602 (kern_return_t)__mach_stack_logging_enumerate_records (lldb_info.task, (uint64_t)0x%x, callback, &lldb_info);
603 lldb_info''' % (options.max_frames, options.max_history, addr)
605 frame = lldb.debugger.GetSelectedTarget().GetProcess(
606 ).GetSelectedThread().GetSelectedFrame()
607 if history:
608 expr = history_expr
609 else:
610 expr = single_expr
611 expr_options = lldb.SBExpressionOptions()
612 expr_options.SetIgnoreBreakpoints(True)
613 expr_options.SetTimeoutInMicroSeconds(5 * 1000 * 1000) # 5 second timeout
614 expr_options.SetTryAllThreads(True)
615 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
616 expr_options.SetPrefix(expr_prefix)
617 expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
618 if options.verbose:
619 print("expression:")
620 print(expr)
621 print("expression result:")
622 print(expr_sbvalue)
623 if expr_sbvalue.error.Success():
624 if history:
625 malloc_stack_history = lldb.value(expr_sbvalue)
626 num_stacks = int(malloc_stack_history.idx)
627 if num_stacks <= options.max_history:
628 i_max = num_stacks
629 else:
630 i_max = options.max_history
631 for i in range(i_max):
632 stack_history_entry = malloc_stack_history.entries[i]
633 dump_stack_history_entry(
634 options, result, stack_history_entry, i)
635 if num_stacks > options.max_history:
636 result.AppendMessage(
637 'warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' %
638 (options.max_history, num_stacks))
639 else:
640 stack_history_entry = lldb.value(expr_sbvalue)
641 dump_stack_history_entry(options, result, stack_history_entry, 0)
643 else:
644 result.AppendMessage(
645 'error: expression failed "%s" => %s' %
646 (expr, expr_sbvalue.error))
649 def display_match_results(
650 process,
651 result,
652 options,
653 arg_str_description,
654 expr,
655 print_no_matches,
656 expr_prefix=None):
657 frame = lldb.debugger.GetSelectedTarget().GetProcess(
658 ).GetSelectedThread().GetSelectedFrame()
659 if not frame:
660 result.AppendMessage('error: invalid frame')
661 return 0
662 expr_options = lldb.SBExpressionOptions()
663 expr_options.SetIgnoreBreakpoints(True)
664 expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues)
665 expr_options.SetTimeoutInMicroSeconds(
666 30 * 1000 * 1000) # 30 second timeout
667 expr_options.SetTryAllThreads(False)
668 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
669 if expr_prefix:
670 expr_options.SetPrefix(expr_prefix)
671 expr_sbvalue = frame.EvaluateExpression(expr, expr_options)
672 if options.verbose:
673 print("expression:")
674 print(expr)
675 print("expression result:")
676 print(expr_sbvalue)
677 if expr_sbvalue.error.Success():
678 match_value = lldb.value(expr_sbvalue)
679 i = 0
680 match_idx = 0
681 while True:
682 print_entry = True
683 match_entry = match_value[i]
684 i += 1
685 if i > options.max_matches:
686 result.AppendMessage(
687 'warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' %
688 (options.max_matches))
689 break
690 malloc_addr = match_entry.addr.sbvalue.unsigned
691 if malloc_addr == 0:
692 break
693 malloc_size = int(match_entry.size)
694 offset = int(match_entry.offset)
696 if options.offset >= 0 and options.offset != offset:
697 print_entry = False
698 else:
699 match_addr = malloc_addr + offset
700 type_flags = int(match_entry.type)
701 #result.AppendMessage (hex(malloc_addr + offset))
702 if type_flags == 64:
703 search_stack_old = options.search_stack
704 search_segments_old = options.search_segments
705 search_heap_old = options.search_heap
706 search_vm_regions = options.search_vm_regions
707 options.search_stack = True
708 options.search_segments = True
709 options.search_heap = True
710 options.search_vm_regions = False
711 if malloc_info_impl(lldb.debugger, result, options, [
712 hex(malloc_addr + offset)]):
713 print_entry = False
714 options.search_stack = search_stack_old
715 options.search_segments = search_segments_old
716 options.search_heap = search_heap_old
717 options.search_vm_regions = search_vm_regions
718 if print_entry:
719 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(
720 process, type_flags, malloc_addr, malloc_size, offset, match_addr))
721 if options.show_size:
722 description += ' <%5u>' % (malloc_size)
723 if options.show_range:
724 description += ' [%#x - %#x)' % (
725 malloc_addr, malloc_addr + malloc_size)
726 derefed_dynamic_value = None
727 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(
728 lldb.eDynamicCanRunTarget)
729 if dynamic_value.type.name == 'void *':
730 if options.type == 'pointer' and malloc_size == 4096:
731 error = lldb.SBError()
732 process = expr_sbvalue.GetProcess()
733 target = expr_sbvalue.GetTarget()
734 data = bytearray(
735 process.ReadMemory(
736 malloc_addr, 16, error))
737 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!':
738 ptr_size = target.addr_size
739 thread = process.ReadUnsignedFromMemory(
740 malloc_addr + 16 + ptr_size, ptr_size, error)
741 # 4 bytes 0xa1a1a1a1
742 # 12 bytes 'AUTORELEASE!'
743 # ptr bytes autorelease insertion point
744 # ptr bytes pthread_t
745 # ptr bytes next colder page
746 # ptr bytes next hotter page
747 # 4 bytes this page's depth in the list
748 # 4 bytes high-water mark
749 description += ' AUTORELEASE! for pthread_t %#x' % (
750 thread)
751 # else:
752 # description += 'malloc(%u)' % (malloc_size)
753 # else:
754 # description += 'malloc(%u)' % (malloc_size)
755 else:
756 derefed_dynamic_value = dynamic_value.deref
757 if derefed_dynamic_value:
758 derefed_dynamic_type = derefed_dynamic_value.type
759 derefed_dynamic_type_size = derefed_dynamic_type.size
760 derefed_dynamic_type_name = derefed_dynamic_type.name
761 description += ' '
762 description += derefed_dynamic_type_name
763 if offset < derefed_dynamic_type_size:
764 member_list = list()
765 get_member_types_for_offset(
766 derefed_dynamic_type, offset, member_list)
767 if member_list:
768 member_path = ''
769 for member in member_list:
770 member_name = member.name
771 if member_name:
772 if member_path:
773 member_path += '.'
774 member_path += member_name
775 if member_path:
776 if options.ivar_regex_exclusions:
777 for ivar_regex in options.ivar_regex_exclusions:
778 if ivar_regex.match(
779 member_path):
780 print_entry = False
781 description += '.%s' % (member_path)
782 else:
783 description += '%u bytes after %s' % (
784 offset - derefed_dynamic_type_size, derefed_dynamic_type_name)
785 else:
786 # strip the "*" from the end of the name since we
787 # were unable to dereference this
788 description += dynamic_value.type.name[0:-1]
789 if print_entry:
790 match_idx += 1
791 result_output = ''
792 if description:
793 result_output += description
794 if options.print_type and derefed_dynamic_value:
795 result_output += ' %s' % (derefed_dynamic_value)
796 if options.print_object_description and dynamic_value:
797 desc = dynamic_value.GetObjectDescription()
798 if desc:
799 result_output += '\n%s' % (desc)
800 if result_output:
801 result.AppendMessage(result_output)
802 if options.memory:
803 cmd_result = lldb.SBCommandReturnObject()
804 if options.format is None:
805 memory_command = "memory read --force 0x%x 0x%x" % (
806 malloc_addr, malloc_addr + malloc_size)
807 else:
808 memory_command = "memory read --force -f %s 0x%x 0x%x" % (
809 options.format, malloc_addr, malloc_addr + malloc_size)
810 if options.verbose:
811 result.AppendMessage(memory_command)
812 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
813 result.AppendMessage(cmd_result.GetOutput())
814 if options.stack_history:
815 dump_stack_history_entries(options, result, malloc_addr, 1)
816 elif options.stack:
817 dump_stack_history_entries(options, result, malloc_addr, 0)
818 return i
819 else:
820 result.AppendMessage(str(expr_sbvalue.error))
821 return 0
824 def get_ptr_refs_options():
825 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
826 description = '''Searches all allocations on the heap for pointer values on
827 darwin user space programs. Any matches that were found will dump the malloc
828 blocks that contain the pointers and might be able to print what kind of
829 objects the pointers are contained in using dynamic type information in the
830 program.'''
831 parser = optparse.OptionParser(
832 description=description,
833 prog='ptr_refs',
834 usage=usage)
835 add_common_options(parser)
836 return parser
839 def find_variable(debugger, command, result, dict):
840 usage = "usage: %prog [options] <ADDR> [ADDR ...]"
841 description = '''Searches for a local variable in all frames that contains a hex ADDR.'''
842 command_args = shlex.split(command)
843 parser = optparse.OptionParser(
844 description=description,
845 prog='find_variable',
846 usage=usage)
847 parser.add_option(
848 '-v',
849 '--verbose',
850 action='store_true',
851 dest='verbose',
852 help='display verbose debug info',
853 default=False)
854 try:
855 (options, args) = parser.parse_args(command_args)
856 except:
857 return
859 process = debugger.GetSelectedTarget().GetProcess()
860 if not process:
861 result.AppendMessage('error: invalid process')
862 return
864 for arg in args:
865 var_addr = int(arg, 16)
866 print("Finding a variable with address %#x..." % (var_addr), file=result)
867 done = False
868 for thread in process:
869 for frame in thread:
870 var = find_variable_containing_address(
871 options.verbose, frame, var_addr)
872 if var:
873 print(var)
874 done = True
875 break
876 if done:
877 break
880 def ptr_refs(debugger, command, result, dict):
881 command_args = shlex.split(command)
882 parser = get_ptr_refs_options()
883 try:
884 (options, args) = parser.parse_args(command_args)
885 except:
886 return
888 process = debugger.GetSelectedTarget().GetProcess()
889 if not process:
890 result.AppendMessage('error: invalid process')
891 return
892 frame = process.GetSelectedThread().GetSelectedFrame()
893 if not frame:
894 result.AppendMessage('error: invalid frame')
895 return
897 options.type = 'pointer'
898 if options.format is None:
899 options.format = "A" # 'A' is "address" format
901 if args:
902 # When we initialize the expression, we must define any types that
903 # we will need when looking at every allocation. We must also define
904 # a type named callback_baton_t and make an instance named "baton"
905 # and initialize it how ever we want to. The address of "baton" will
906 # be passed into our range callback. callback_baton_t must contain
907 # a member named "callback" whose type is "range_callback_t". This
908 # will be used by our zone callbacks to call the range callback for
909 # each malloc range.
910 expr_prefix = '''
911 struct $malloc_match {
912 void *addr;
913 uintptr_t size;
914 uintptr_t offset;
915 uintptr_t type;
918 user_init_code_format = '''
919 #define MAX_MATCHES %u
920 typedef struct callback_baton_t {
921 range_callback_t callback;
922 unsigned num_matches;
923 $malloc_match matches[MAX_MATCHES];
924 void *ptr;
925 } callback_baton_t;
926 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
927 callback_baton_t *lldb_info = (callback_baton_t *)baton;
928 typedef void* T;
929 const unsigned size = sizeof(T);
930 T *array = (T*)ptr_addr;
931 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) {
932 if (array[idx] == lldb_info->ptr) {
933 if (lldb_info->num_matches < MAX_MATCHES) {
934 lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
935 lldb_info->matches[lldb_info->num_matches].size = ptr_size;
936 lldb_info->matches[lldb_info->num_matches].offset = idx*sizeof(T);
937 lldb_info->matches[lldb_info->num_matches].type = type;
938 ++lldb_info->num_matches;
943 callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
945 # We must also define a snippet of code to be run that returns
946 # the result of the expression we run.
947 # Here we return NULL if our pointer was not found in any malloc blocks,
948 # and we return the address of the matches array so we can then access
949 # the matching results
950 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
951 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
952 baton.matches'''
953 # Iterate through all of our pointer expressions and display the
954 # results
955 for ptr_expr in args:
956 user_init_code = user_init_code_format % (
957 options.max_matches, ptr_expr)
958 expr = get_iterate_memory_expr(
959 options, process, user_init_code, user_return_code)
960 arg_str_description = 'malloc block containing pointer %s' % ptr_expr
961 display_match_results(
962 process,
963 result,
964 options,
965 arg_str_description,
966 expr,
967 True,
968 expr_prefix)
969 else:
970 result.AppendMessage('error: no pointer arguments were given')
973 def get_cstr_refs_options():
974 usage = "usage: %prog [options] <CSTR> [CSTR ...]"
975 description = '''Searches all allocations on the heap for C string values on
976 darwin user space programs. Any matches that were found will dump the malloc
977 blocks that contain the C strings and might be able to print what kind of
978 objects the pointers are contained in using dynamic type information in the
979 program.'''
980 parser = optparse.OptionParser(
981 description=description,
982 prog='cstr_refs',
983 usage=usage)
984 add_common_options(parser)
985 return parser
988 def cstr_refs(debugger, command, result, dict):
989 command_args = shlex.split(command)
990 parser = get_cstr_refs_options()
991 try:
992 (options, args) = parser.parse_args(command_args)
993 except:
994 return
996 process = debugger.GetSelectedTarget().GetProcess()
997 if not process:
998 result.AppendMessage('error: invalid process')
999 return
1000 frame = process.GetSelectedThread().GetSelectedFrame()
1001 if not frame:
1002 result.AppendMessage('error: invalid frame')
1003 return
1005 options.type = 'cstr'
1006 if options.format is None:
1007 options.format = "Y" # 'Y' is "bytes with ASCII" format
1009 if args:
1010 # When we initialize the expression, we must define any types that
1011 # we will need when looking at every allocation. We must also define
1012 # a type named callback_baton_t and make an instance named "baton"
1013 # and initialize it how ever we want to. The address of "baton" will
1014 # be passed into our range callback. callback_baton_t must contain
1015 # a member named "callback" whose type is "range_callback_t". This
1016 # will be used by our zone callbacks to call the range callback for
1017 # each malloc range.
1018 expr_prefix = '''
1019 struct $malloc_match {
1020 void *addr;
1021 uintptr_t size;
1022 uintptr_t offset;
1023 uintptr_t type;
1026 user_init_code_format = '''
1027 #define MAX_MATCHES %u
1028 typedef struct callback_baton_t {
1029 range_callback_t callback;
1030 unsigned num_matches;
1031 $malloc_match matches[MAX_MATCHES];
1032 const char *cstr;
1033 unsigned cstr_len;
1034 } callback_baton_t;
1035 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1036 callback_baton_t *lldb_info = (callback_baton_t *)baton;
1037 if (lldb_info->cstr_len < ptr_size) {
1038 const char *begin = (const char *)ptr_addr;
1039 const char *end = begin + ptr_size - lldb_info->cstr_len;
1040 for (const char *s = begin; s < end; ++s) {
1041 if ((int)memcmp(s, lldb_info->cstr, lldb_info->cstr_len) == 0) {
1042 if (lldb_info->num_matches < MAX_MATCHES) {
1043 lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1044 lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1045 lldb_info->matches[lldb_info->num_matches].offset = s - begin;
1046 lldb_info->matches[lldb_info->num_matches].type = type;
1047 ++lldb_info->num_matches;
1053 const char *cstr = "%s";
1054 callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };'''
1055 # We must also define a snippet of code to be run that returns
1056 # the result of the expression we run.
1057 # Here we return NULL if our pointer was not found in any malloc blocks,
1058 # and we return the address of the matches array so we can then access
1059 # the matching results
1060 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1061 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1062 baton.matches'''
1063 # Iterate through all of our pointer expressions and display the
1064 # results
1065 for cstr in args:
1066 user_init_code = user_init_code_format % (
1067 options.max_matches, cstr)
1068 expr = get_iterate_memory_expr(
1069 options, process, user_init_code, user_return_code)
1070 arg_str_description = 'malloc block containing "%s"' % cstr
1071 display_match_results(
1072 process,
1073 result,
1074 options,
1075 arg_str_description,
1076 expr,
1077 True,
1078 expr_prefix)
1079 else:
1080 result.AppendMessage(
1081 'error: command takes one or more C string arguments')
1084 def get_malloc_info_options():
1085 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1086 description = '''Searches the heap a malloc block that contains the addresses
1087 specified as one or more address expressions. Any matches that were found will
1088 dump the malloc blocks that match or contain the specified address. The matching
1089 blocks might be able to show what kind of objects they are using dynamic type
1090 information in the program.'''
1091 parser = optparse.OptionParser(
1092 description=description,
1093 prog='malloc_info',
1094 usage=usage)
1095 add_common_options(parser)
1096 return parser
1099 def malloc_info(debugger, command, result, dict):
1100 command_args = shlex.split(command)
1101 parser = get_malloc_info_options()
1102 try:
1103 (options, args) = parser.parse_args(command_args)
1104 except:
1105 return
1106 malloc_info_impl(debugger, result, options, args)
1109 def malloc_info_impl(debugger, result, options, args):
1110 # We are specifically looking for something on the heap only
1111 options.type = 'malloc_info'
1113 process = debugger.GetSelectedTarget().GetProcess()
1114 if not process:
1115 result.AppendMessage('error: invalid process')
1116 return
1117 frame = process.GetSelectedThread().GetSelectedFrame()
1118 if not frame:
1119 result.AppendMessage('error: invalid frame')
1120 return
1121 expr_prefix = '''
1122 struct $malloc_match {
1123 void *addr;
1124 uintptr_t size;
1125 uintptr_t offset;
1126 uintptr_t type;
1130 user_init_code_format = '''
1131 typedef struct callback_baton_t {
1132 range_callback_t callback;
1133 unsigned num_matches;
1134 $malloc_match matches[2]; // Two items so they can be NULL terminated
1135 void *ptr;
1136 } callback_baton_t;
1137 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1138 callback_baton_t *lldb_info = (callback_baton_t *)baton;
1139 if (lldb_info->num_matches == 0) {
1140 uint8_t *p = (uint8_t *)lldb_info->ptr;
1141 uint8_t *lo = (uint8_t *)ptr_addr;
1142 uint8_t *hi = lo + ptr_size;
1143 if (lo <= p && p < hi) {
1144 lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1145 lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1146 lldb_info->matches[lldb_info->num_matches].offset = p - lo;
1147 lldb_info->matches[lldb_info->num_matches].type = type;
1148 lldb_info->num_matches = 1;
1152 callback_baton_t baton = { range_callback, 0, {0}, (void *)%s };
1153 baton.matches[0].addr = 0;
1154 baton.matches[1].addr = 0;'''
1155 if args:
1156 total_matches = 0
1157 for ptr_expr in args:
1158 user_init_code = user_init_code_format % (ptr_expr)
1159 expr = get_iterate_memory_expr(
1160 options, process, user_init_code, 'baton.matches')
1161 arg_str_description = 'malloc block that contains %s' % ptr_expr
1162 total_matches += display_match_results(
1163 process, result, options, arg_str_description, expr, True, expr_prefix)
1164 return total_matches
1165 else:
1166 result.AppendMessage(
1167 'error: command takes one or more pointer expressions')
1168 return 0
1171 def get_thread_stack_ranges_struct(process):
1172 '''Create code that defines a structure that represents threads stack bounds
1173 for all threads. It returns a static sized array initialized with all of
1174 the tid, base, size structs for all the threads.'''
1175 stack_dicts = list()
1176 if process:
1177 i = 0
1178 for thread in process:
1179 min_sp = thread.frame[0].sp
1180 max_sp = min_sp
1181 for frame in thread.frames:
1182 sp = frame.sp
1183 if sp < min_sp:
1184 min_sp = sp
1185 if sp > max_sp:
1186 max_sp = sp
1187 if min_sp < max_sp:
1188 stack_dicts.append({'tid': thread.GetThreadID(
1189 ), 'base': min_sp, 'size': max_sp - min_sp, 'index': i})
1190 i += 1
1191 stack_dicts_len = len(stack_dicts)
1192 if stack_dicts_len > 0:
1193 result = '''
1194 #define NUM_STACKS %u
1195 #define STACK_RED_ZONE_SIZE %u
1196 typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t;
1197 thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize())
1198 for stack_dict in stack_dicts:
1199 result += '''
1200 stacks[%(index)u].tid = 0x%(tid)x;
1201 stacks[%(index)u].base = 0x%(base)x;
1202 stacks[%(index)u].size = 0x%(size)x;''' % stack_dict
1203 return result
1204 else:
1205 return ''
1208 def get_sections_ranges_struct(process):
1209 '''Create code that defines a structure that represents all segments that
1210 can contain data for all images in "target". It returns a static sized
1211 array initialized with all of base, size structs for all the threads.'''
1212 target = process.target
1213 segment_dicts = list()
1214 for (module_idx, module) in enumerate(target.modules):
1215 for sect_idx in range(module.GetNumSections()):
1216 section = module.GetSectionAtIndex(sect_idx)
1217 if not section:
1218 break
1219 name = section.name
1220 if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO':
1221 base = section.GetLoadAddress(target)
1222 size = section.GetByteSize()
1223 if base != lldb.LLDB_INVALID_ADDRESS and size > 0:
1224 segment_dicts.append({'base': base, 'size': size})
1225 segment_dicts_len = len(segment_dicts)
1226 if segment_dicts_len > 0:
1227 result = '''
1228 #define NUM_SEGMENTS %u
1229 typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t;
1230 segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,)
1231 for (idx, segment_dict) in enumerate(segment_dicts):
1232 segment_dict['index'] = idx
1233 result += '''
1234 segments[%(index)u].base = 0x%(base)x;
1235 segments[%(index)u].size = 0x%(size)x;''' % segment_dict
1236 return result
1237 else:
1238 return ''
1241 def section_ptr_refs(debugger, command, result, dict):
1242 command_args = shlex.split(command)
1243 usage = "usage: %prog [options] <EXPR> [EXPR ...]"
1244 description = '''Searches section contents for pointer values in darwin user space programs.'''
1245 parser = optparse.OptionParser(
1246 description=description,
1247 prog='section_ptr_refs',
1248 usage=usage)
1249 add_common_options(parser)
1250 parser.add_option(
1251 '--section',
1252 action='append',
1253 type='string',
1254 dest='section_names',
1255 help='section name to search',
1256 default=list())
1257 try:
1258 (options, args) = parser.parse_args(command_args)
1259 except:
1260 return
1262 options.type = 'pointer'
1264 sections = list()
1265 section_modules = list()
1266 if not options.section_names:
1267 result.AppendMessage(
1268 'error: at least one section must be specified with the --section option')
1269 return
1271 target = debugger.GetSelectedTarget()
1272 for module in target.modules:
1273 for section_name in options.section_names:
1274 section = module.section[section_name]
1275 if section:
1276 sections.append(section)
1277 section_modules.append(module)
1278 if sections:
1279 dylid_load_err = load_dylib()
1280 if dylid_load_err:
1281 result.AppendMessage(dylid_load_err)
1282 return
1283 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame()
1284 for expr_str in args:
1285 for (idx, section) in enumerate(sections):
1286 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (
1287 section.addr.load_addr, section.size, expr_str)
1288 arg_str_description = 'section %s.%s containing "%s"' % (
1289 section_modules[idx].file.fullpath, section.name, expr_str)
1290 num_matches = display_match_results(
1291 target.GetProcess(), result, options, arg_str_description, expr, False)
1292 if num_matches:
1293 if num_matches < options.max_matches:
1294 options.max_matches = options.max_matches - num_matches
1295 else:
1296 options.max_matches = 0
1297 if options.max_matches == 0:
1298 return
1299 else:
1300 result.AppendMessage(
1301 'error: no sections were found that match any of %s' %
1302 (', '.join(
1303 options.section_names)))
1306 def get_objc_refs_options():
1307 usage = "usage: %prog [options] <CLASS> [CLASS ...]"
1308 description = '''Searches all allocations on the heap for instances of
1309 objective C classes, or any classes that inherit from the specified classes
1310 in darwin user space programs. Any matches that were found will dump the malloc
1311 blocks that contain the C strings and might be able to print what kind of
1312 objects the pointers are contained in using dynamic type information in the
1313 program.'''
1314 parser = optparse.OptionParser(
1315 description=description,
1316 prog='objc_refs',
1317 usage=usage)
1318 add_common_options(parser)
1319 return parser
1322 def objc_refs(debugger, command, result, dict):
1323 command_args = shlex.split(command)
1324 parser = get_objc_refs_options()
1325 try:
1326 (options, args) = parser.parse_args(command_args)
1327 except:
1328 return
1330 process = debugger.GetSelectedTarget().GetProcess()
1331 if not process:
1332 result.AppendMessage('error: invalid process')
1333 return
1334 frame = process.GetSelectedThread().GetSelectedFrame()
1335 if not frame:
1336 result.AppendMessage('error: invalid frame')
1337 return
1339 options.type = 'isa'
1340 if options.format is None:
1341 options.format = "A" # 'A' is "address" format
1343 expr_options = lldb.SBExpressionOptions()
1344 expr_options.SetIgnoreBreakpoints(True)
1345 expr_options.SetTimeoutInMicroSeconds(
1346 3 * 1000 * 1000) # 3 second infinite timeout
1347 expr_options.SetTryAllThreads(True)
1348 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1349 num_objc_classes_value = frame.EvaluateExpression(
1350 "(int)objc_getClassList((void *)0, (int)0)", expr_options)
1351 if not num_objc_classes_value.error.Success():
1352 result.AppendMessage('error: %s' %
1353 num_objc_classes_value.error.GetCString())
1354 return
1356 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned()
1357 if num_objc_classes == 0:
1358 result.AppendMessage('error: no objective C classes in program')
1359 return
1361 if args:
1362 # When we initialize the expression, we must define any types that
1363 # we will need when looking at every allocation. We must also define
1364 # a type named callback_baton_t and make an instance named "baton"
1365 # and initialize it how ever we want to. The address of "baton" will
1366 # be passed into our range callback. callback_baton_t must contain
1367 # a member named "callback" whose type is "range_callback_t". This
1368 # will be used by our zone callbacks to call the range callback for
1369 # each malloc range.
1370 expr_prefix = '''
1371 struct $malloc_match {
1372 void *addr;
1373 uintptr_t size;
1374 uintptr_t offset;
1375 uintptr_t type;
1379 user_init_code_format = '''
1380 #define MAX_MATCHES %u
1381 typedef int (*compare_callback_t)(const void *a, const void *b);
1382 typedef struct callback_baton_t {
1383 range_callback_t callback;
1384 compare_callback_t compare_callback;
1385 unsigned num_matches;
1386 $malloc_match matches[MAX_MATCHES];
1387 void *isa;
1388 Class classes[%u];
1389 } callback_baton_t;
1390 compare_callback_t compare_callback = [](const void *a, const void *b) -> int {
1391 Class a_ptr = *(Class *)a;
1392 Class b_ptr = *(Class *)b;
1393 if (a_ptr < b_ptr) return -1;
1394 if (a_ptr > b_ptr) return +1;
1395 return 0;
1397 typedef Class (*class_getSuperclass_type)(void *isa);
1398 range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void {
1399 class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass;
1400 callback_baton_t *lldb_info = (callback_baton_t *)baton;
1401 if (sizeof(Class) <= ptr_size) {
1402 Class *curr_class_ptr = (Class *)ptr_addr;
1403 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr,
1404 (const void *)lldb_info->classes,
1405 sizeof(lldb_info->classes)/sizeof(Class),
1406 sizeof(Class),
1407 lldb_info->compare_callback);
1408 if (matching_class_ptr) {
1409 bool match = false;
1410 if (lldb_info->isa) {
1411 Class isa = *curr_class_ptr;
1412 if (lldb_info->isa == isa)
1413 match = true;
1414 else { // if (lldb_info->objc.match_superclasses) {
1415 Class super = class_getSuperclass_impl(isa);
1416 while (super) {
1417 if (super == lldb_info->isa) {
1418 match = true;
1419 break;
1421 super = class_getSuperclass_impl(super);
1425 else
1426 match = true;
1427 if (match) {
1428 if (lldb_info->num_matches < MAX_MATCHES) {
1429 lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr;
1430 lldb_info->matches[lldb_info->num_matches].size = ptr_size;
1431 lldb_info->matches[lldb_info->num_matches].offset = 0;
1432 lldb_info->matches[lldb_info->num_matches].type = type;
1433 ++lldb_info->num_matches;
1439 callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} };
1440 int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class));
1441 (void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);'''
1442 # We must also define a snippet of code to be run that returns
1443 # the result of the expression we run.
1444 # Here we return NULL if our pointer was not found in any malloc blocks,
1445 # and we return the address of the matches array so we can then access
1446 # the matching results
1447 user_return_code = '''if (baton.num_matches < MAX_MATCHES)
1448 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array
1449 baton.matches'''
1450 # Iterate through all of our ObjC class name arguments
1451 for class_name in args:
1452 addr_expr_str = "(void *)[%s class]" % class_name
1453 expr_options = lldb.SBExpressionOptions()
1454 expr_options.SetIgnoreBreakpoints(True)
1455 expr_options.SetTimeoutInMicroSeconds(
1456 1 * 1000 * 1000) # 1 second timeout
1457 expr_options.SetTryAllThreads(True)
1458 expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus)
1459 expr_sbvalue = frame.EvaluateExpression(
1460 addr_expr_str, expr_options)
1461 if expr_sbvalue.error.Success():
1462 isa = expr_sbvalue.unsigned
1463 if isa:
1464 options.type = 'isa'
1465 result.AppendMessage(
1466 'Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' %
1467 (class_name, isa))
1468 user_init_code = user_init_code_format % (
1469 options.max_matches, num_objc_classes, isa)
1470 expr = get_iterate_memory_expr(
1471 options, process, user_init_code, user_return_code)
1472 arg_str_description = 'objective C classes with isa 0x%x' % isa
1473 display_match_results(
1474 process,
1475 result,
1476 options,
1477 arg_str_description,
1478 expr,
1479 True,
1480 expr_prefix)
1481 else:
1482 result.AppendMessage(
1483 'error: Can\'t find isa for an ObjC class named "%s"' %
1484 (class_name))
1485 else:
1486 result.AppendMessage(
1487 'error: expression error for "%s": %s' %
1488 (addr_expr_str, expr_sbvalue.error))
1489 else:
1490 result.AppendMessage(
1491 'error: command takes one or more C string arguments')
1493 if __name__ == '__main__':
1494 lldb.debugger = lldb.SBDebugger.Create()
1496 def __lldb_init_module(debugger, internal_dict):
1497 # Make the options so we can generate the help text for the new LLDB
1498 # command line command prior to registering it with LLDB below. This way
1499 # if clients in LLDB type "help malloc_info", they will see the exact same
1500 # output as typing "malloc_info --help".
1501 ptr_refs.__doc__ = get_ptr_refs_options().format_help()
1502 cstr_refs.__doc__ = get_cstr_refs_options().format_help()
1503 malloc_info.__doc__ = get_malloc_info_options().format_help()
1504 objc_refs.__doc__ = get_objc_refs_options().format_help()
1505 debugger.HandleCommand(
1506 'command script add -f %s.ptr_refs ptr_refs' %
1507 __name__)
1508 debugger.HandleCommand(
1509 'command script add -f %s.cstr_refs cstr_refs' %
1510 __name__)
1511 debugger.HandleCommand(
1512 'command script add -f %s.malloc_info malloc_info' %
1513 __name__)
1514 debugger.HandleCommand(
1515 'command script add -f %s.find_variable find_variable' %
1516 __name__)
1517 # debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name)
1518 debugger.HandleCommand(
1519 'command script add -f %s.objc_refs objc_refs' %
1520 __name__)
1521 print('"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.')