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