1be691f3bSpatrick#!/usr/bin/env python3 2061da546Spatrick 3061da546Spatrick#---------------------------------------------------------------------- 4061da546Spatrick# This module is designed to live inside the "lldb" python package 5061da546Spatrick# in the "lldb.macosx" package. To use this in the embedded python 6061da546Spatrick# interpreter using "lldb" just import it: 7061da546Spatrick# 8061da546Spatrick# (lldb) script import lldb.macosx.heap 9061da546Spatrick#---------------------------------------------------------------------- 10061da546Spatrick 11061da546Spatrickimport lldb 12061da546Spatrickimport optparse 13061da546Spatrickimport os 14061da546Spatrickimport os.path 15061da546Spatrickimport re 16061da546Spatrickimport shlex 17061da546Spatrickimport string 18061da546Spatrickimport sys 19061da546Spatrickimport tempfile 20061da546Spatrickimport lldb.utils.symbolication 21061da546Spatrick 22061da546Spatrickg_libheap_dylib_dir = None 23061da546Spatrickg_libheap_dylib_dict = dict() 24061da546Spatrick 25061da546Spatrick 26061da546Spatrickdef get_iterate_memory_expr( 27061da546Spatrick options, 28061da546Spatrick process, 29061da546Spatrick user_init_code, 30061da546Spatrick user_return_code): 31061da546Spatrick expr = ''' 32061da546Spatricktypedef unsigned natural_t; 33061da546Spatricktypedef uintptr_t vm_size_t; 34061da546Spatricktypedef uintptr_t vm_address_t; 35061da546Spatricktypedef natural_t task_t; 36061da546Spatricktypedef int kern_return_t; 37061da546Spatrick#define KERN_SUCCESS 0 38be691f3bSpatricktypedef void (*range_callback_t)(task_t, void *, unsigned, uintptr_t, uintptr_t); 39061da546Spatrick''' 40061da546Spatrick if options.search_vm_regions: 41061da546Spatrick expr += ''' 42061da546Spatricktypedef int vm_prot_t; 43061da546Spatricktypedef unsigned int vm_inherit_t; 44061da546Spatricktypedef unsigned long long memory_object_offset_t; 45061da546Spatricktypedef unsigned int boolean_t; 46061da546Spatricktypedef int vm_behavior_t; 47061da546Spatricktypedef uint32_t vm32_object_id_t; 48061da546Spatricktypedef natural_t mach_msg_type_number_t; 49061da546Spatricktypedef uint64_t mach_vm_address_t; 50061da546Spatricktypedef uint64_t mach_vm_offset_t; 51061da546Spatricktypedef uint64_t mach_vm_size_t; 52061da546Spatricktypedef uint64_t vm_map_offset_t; 53061da546Spatricktypedef uint64_t vm_map_address_t; 54061da546Spatricktypedef uint64_t vm_map_size_t; 55061da546Spatrick#define VM_PROT_NONE ((vm_prot_t) 0x00) 56061da546Spatrick#define VM_PROT_READ ((vm_prot_t) 0x01) 57061da546Spatrick#define VM_PROT_WRITE ((vm_prot_t) 0x02) 58061da546Spatrick#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) 59061da546Spatricktypedef struct vm_region_submap_short_info_data_64_t { 60061da546Spatrick vm_prot_t protection; 61061da546Spatrick vm_prot_t max_protection; 62061da546Spatrick vm_inherit_t inheritance; 63061da546Spatrick memory_object_offset_t offset; // offset into object/map 64061da546Spatrick unsigned int user_tag; // user tag on map entry 65061da546Spatrick unsigned int ref_count; // obj/map mappers, etc 66061da546Spatrick unsigned short shadow_depth; // only for obj 67061da546Spatrick unsigned char external_pager; // only for obj 68061da546Spatrick unsigned char share_mode; // see enumeration 69061da546Spatrick boolean_t is_submap; // submap vs obj 70061da546Spatrick vm_behavior_t behavior; // access behavior hint 71061da546Spatrick vm32_object_id_t object_id; // obj/map name, not a handle 72061da546Spatrick unsigned short user_wired_count; 73061da546Spatrick} vm_region_submap_short_info_data_64_t; 74061da546Spatrick#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))''' 75061da546Spatrick if user_init_code: 76061da546Spatrick expr += user_init_code 77061da546Spatrick expr += ''' 78061da546Spatricktask_t task = (task_t)mach_task_self(); 79061da546Spatrickmach_vm_address_t vm_region_base_addr; 80061da546Spatrickmach_vm_size_t vm_region_size; 81061da546Spatricknatural_t vm_region_depth; 82061da546Spatrickvm_region_submap_short_info_data_64_t vm_region_info; 83061da546Spatrickkern_return_t err; 84061da546Spatrickfor (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) 85061da546Spatrick{ 86061da546Spatrick mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; 87061da546Spatrick err = (kern_return_t)mach_vm_region_recurse (task, 88061da546Spatrick &vm_region_base_addr, 89061da546Spatrick &vm_region_size, 90061da546Spatrick &vm_region_depth, 91061da546Spatrick &vm_region_info, 92061da546Spatrick &vm_region_info_size); 93061da546Spatrick if (err) 94061da546Spatrick break; 95061da546Spatrick // Check all read + write regions. This will cover the thread stacks 96061da546Spatrick // and any regions of memory like __DATA segments, that might contain 97061da546Spatrick // data we are looking for 98061da546Spatrick if (vm_region_info.protection & VM_PROT_WRITE && 99061da546Spatrick vm_region_info.protection & VM_PROT_READ) 100061da546Spatrick { 101061da546Spatrick baton.callback (task, 102061da546Spatrick &baton, 103061da546Spatrick 64, 104061da546Spatrick vm_region_base_addr, 105061da546Spatrick vm_region_size); 106061da546Spatrick } 107061da546Spatrick}''' 108061da546Spatrick else: 109061da546Spatrick if options.search_stack: 110061da546Spatrick expr += get_thread_stack_ranges_struct(process) 111061da546Spatrick if options.search_segments: 112061da546Spatrick expr += get_sections_ranges_struct(process) 113061da546Spatrick if user_init_code: 114061da546Spatrick expr += user_init_code 115061da546Spatrick if options.search_heap: 116061da546Spatrick expr += ''' 117061da546Spatrick#define MALLOC_PTR_IN_USE_RANGE_TYPE 1 118061da546Spatricktypedef struct vm_range_t { 119061da546Spatrick vm_address_t address; 120061da546Spatrick vm_size_t size; 121061da546Spatrick} vm_range_t; 122be691f3bSpatricktypedef kern_return_t (*memory_reader_t)(task_t, vm_address_t, vm_size_t, void **); 123be691f3bSpatricktypedef void (*vm_range_recorder_t)(task_t, void *, unsigned, vm_range_t *, unsigned); 124061da546Spatricktypedef struct malloc_introspection_t { 125061da546Spatrick 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 */ 126061da546Spatrick} malloc_introspection_t; 127061da546Spatricktypedef struct malloc_zone_t { 128061da546Spatrick void *reserved1[12]; 129061da546Spatrick struct malloc_introspection_t *introspect; 130061da546Spatrick} malloc_zone_t; 131*f6aab3d8Srobertkern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *); 132be691f3bSpatrickmemory_reader_t task_peek = [](task_t, vm_address_t remote_address, vm_size_t, void **local_memory) -> kern_return_t { 133061da546Spatrick *local_memory = (void*) remote_address; 134061da546Spatrick return KERN_SUCCESS; 135061da546Spatrick}; 136061da546Spatrickvm_address_t *zones = 0; 137061da546Spatrickunsigned int num_zones = 0;task_t task = 0; 138061da546Spatrickkern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones); 139061da546Spatrickif (KERN_SUCCESS == err) 140061da546Spatrick{ 141061da546Spatrick for (unsigned int i=0; i<num_zones; ++i) 142061da546Spatrick { 143061da546Spatrick const malloc_zone_t *zone = (const malloc_zone_t *)zones[i]; 144061da546Spatrick if (zone && zone->introspect) 145061da546Spatrick zone->introspect->enumerator (task, 146061da546Spatrick &baton, 147061da546Spatrick MALLOC_PTR_IN_USE_RANGE_TYPE, 148061da546Spatrick (vm_address_t)zone, 149061da546Spatrick task_peek, 150061da546Spatrick [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void 151061da546Spatrick { 152061da546Spatrick range_callback_t callback = ((callback_baton_t *)baton)->callback; 153061da546Spatrick for (unsigned i=0; i<size; ++i) 154061da546Spatrick { 155061da546Spatrick callback (task, baton, type, ranges[i].address, ranges[i].size); 156061da546Spatrick } 157061da546Spatrick }); 158061da546Spatrick } 159061da546Spatrick}''' 160061da546Spatrick 161061da546Spatrick if options.search_stack: 162061da546Spatrick expr += ''' 163061da546Spatrick#ifdef NUM_STACKS 164061da546Spatrick// Call the callback for the thread stack ranges 165061da546Spatrickfor (uint32_t i=0; i<NUM_STACKS; ++i) { 166061da546Spatrick range_callback(task, &baton, 8, stacks[i].base, stacks[i].size); 167061da546Spatrick if (STACK_RED_ZONE_SIZE > 0) { 168061da546Spatrick range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE); 169061da546Spatrick } 170061da546Spatrick} 171061da546Spatrick#endif''' 172061da546Spatrick 173061da546Spatrick if options.search_segments: 174061da546Spatrick expr += ''' 175061da546Spatrick#ifdef NUM_SEGMENTS 176061da546Spatrick// Call the callback for all segments 177061da546Spatrickfor (uint32_t i=0; i<NUM_SEGMENTS; ++i) 178061da546Spatrick range_callback(task, &baton, 32, segments[i].base, segments[i].size); 179061da546Spatrick#endif''' 180061da546Spatrick 181061da546Spatrick if user_return_code: 182061da546Spatrick expr += "\n%s" % (user_return_code,) 183061da546Spatrick 184061da546Spatrick return expr 185061da546Spatrick 186061da546Spatrick 187061da546Spatrickdef get_member_types_for_offset(value_type, offset, member_list): 188061da546Spatrick member = value_type.GetFieldAtIndex(0) 189061da546Spatrick search_bases = False 190061da546Spatrick if member: 191061da546Spatrick if member.GetOffsetInBytes() <= offset: 192061da546Spatrick for field_idx in range(value_type.GetNumberOfFields()): 193061da546Spatrick member = value_type.GetFieldAtIndex(field_idx) 194061da546Spatrick member_byte_offset = member.GetOffsetInBytes() 195061da546Spatrick member_end_byte_offset = member_byte_offset + member.type.size 196061da546Spatrick if member_byte_offset <= offset and offset < member_end_byte_offset: 197061da546Spatrick member_list.append(member) 198061da546Spatrick get_member_types_for_offset( 199061da546Spatrick member.type, offset - member_byte_offset, member_list) 200061da546Spatrick return 201061da546Spatrick else: 202061da546Spatrick search_bases = True 203061da546Spatrick else: 204061da546Spatrick search_bases = True 205061da546Spatrick if search_bases: 206061da546Spatrick for field_idx in range(value_type.GetNumberOfDirectBaseClasses()): 207061da546Spatrick member = value_type.GetDirectBaseClassAtIndex(field_idx) 208061da546Spatrick member_byte_offset = member.GetOffsetInBytes() 209061da546Spatrick member_end_byte_offset = member_byte_offset + member.type.size 210061da546Spatrick if member_byte_offset <= offset and offset < member_end_byte_offset: 211061da546Spatrick member_list.append(member) 212061da546Spatrick get_member_types_for_offset( 213061da546Spatrick member.type, offset - member_byte_offset, member_list) 214061da546Spatrick return 215061da546Spatrick for field_idx in range(value_type.GetNumberOfVirtualBaseClasses()): 216061da546Spatrick member = value_type.GetVirtualBaseClassAtIndex(field_idx) 217061da546Spatrick member_byte_offset = member.GetOffsetInBytes() 218061da546Spatrick member_end_byte_offset = member_byte_offset + member.type.size 219061da546Spatrick if member_byte_offset <= offset and offset < member_end_byte_offset: 220061da546Spatrick member_list.append(member) 221061da546Spatrick get_member_types_for_offset( 222061da546Spatrick member.type, offset - member_byte_offset, member_list) 223061da546Spatrick return 224061da546Spatrick 225061da546Spatrick 226061da546Spatrickdef append_regex_callback(option, opt, value, parser): 227061da546Spatrick try: 228061da546Spatrick ivar_regex = re.compile(value) 229dda28197Spatrick parser.values.ivar_regex_exclusions.append(ivar_regex) 230061da546Spatrick except: 231061da546Spatrick print('error: an exception was thrown when compiling the ivar regular expression for "%s"' % value) 232061da546Spatrick 233061da546Spatrick 234061da546Spatrickdef add_common_options(parser): 235061da546Spatrick parser.add_option( 236061da546Spatrick '-v', 237061da546Spatrick '--verbose', 238061da546Spatrick action='store_true', 239061da546Spatrick dest='verbose', 240061da546Spatrick help='display verbose debug info', 241061da546Spatrick default=False) 242061da546Spatrick parser.add_option( 243061da546Spatrick '-t', 244061da546Spatrick '--type', 245061da546Spatrick action='store_true', 246061da546Spatrick dest='print_type', 247061da546Spatrick help='print the full value of the type for each matching malloc block', 248061da546Spatrick default=False) 249061da546Spatrick parser.add_option( 250061da546Spatrick '-o', 251061da546Spatrick '--po', 252061da546Spatrick action='store_true', 253061da546Spatrick dest='print_object_description', 254061da546Spatrick help='print the object descriptions for any matches', 255061da546Spatrick default=False) 256061da546Spatrick parser.add_option( 257061da546Spatrick '-z', 258061da546Spatrick '--size', 259061da546Spatrick action='store_true', 260061da546Spatrick dest='show_size', 261061da546Spatrick help='print the allocation size in bytes', 262061da546Spatrick default=False) 263061da546Spatrick parser.add_option( 264061da546Spatrick '-r', 265061da546Spatrick '--range', 266061da546Spatrick action='store_true', 267061da546Spatrick dest='show_range', 268061da546Spatrick help='print the allocation address range instead of just the allocation base address', 269061da546Spatrick default=False) 270061da546Spatrick parser.add_option( 271061da546Spatrick '-m', 272061da546Spatrick '--memory', 273061da546Spatrick action='store_true', 274061da546Spatrick dest='memory', 275061da546Spatrick help='dump the memory for each matching block', 276061da546Spatrick default=False) 277061da546Spatrick parser.add_option( 278061da546Spatrick '-f', 279061da546Spatrick '--format', 280061da546Spatrick type='string', 281061da546Spatrick dest='format', 282061da546Spatrick help='the format to use when dumping memory if --memory is specified', 283061da546Spatrick default=None) 284061da546Spatrick parser.add_option( 285061da546Spatrick '-I', 286061da546Spatrick '--omit-ivar-regex', 287061da546Spatrick type='string', 288061da546Spatrick action='callback', 289061da546Spatrick callback=append_regex_callback, 290dda28197Spatrick dest='ivar_regex_exclusions', 291061da546Spatrick default=[], 292061da546Spatrick help='specify one or more regular expressions used to backlist any matches that are in ivars') 293061da546Spatrick parser.add_option( 294061da546Spatrick '-s', 295061da546Spatrick '--stack', 296061da546Spatrick action='store_true', 297061da546Spatrick dest='stack', 298061da546Spatrick help='gets the stack that allocated each malloc block if MallocStackLogging is enabled', 299061da546Spatrick default=False) 300061da546Spatrick parser.add_option( 301061da546Spatrick '-S', 302061da546Spatrick '--stack-history', 303061da546Spatrick action='store_true', 304061da546Spatrick dest='stack_history', 305061da546Spatrick help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled', 306061da546Spatrick default=False) 307061da546Spatrick parser.add_option( 308061da546Spatrick '-F', 309061da546Spatrick '--max-frames', 310061da546Spatrick type='int', 311061da546Spatrick dest='max_frames', 312061da546Spatrick help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)', 313061da546Spatrick default=128) 314061da546Spatrick parser.add_option( 315061da546Spatrick '-H', 316061da546Spatrick '--max-history', 317061da546Spatrick type='int', 318061da546Spatrick dest='max_history', 319061da546Spatrick help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)', 320061da546Spatrick default=16) 321061da546Spatrick parser.add_option( 322061da546Spatrick '-M', 323061da546Spatrick '--max-matches', 324061da546Spatrick type='int', 325061da546Spatrick dest='max_matches', 326061da546Spatrick help='the maximum number of matches to print', 327061da546Spatrick default=32) 328061da546Spatrick parser.add_option( 329061da546Spatrick '-O', 330061da546Spatrick '--offset', 331061da546Spatrick type='int', 332061da546Spatrick dest='offset', 333061da546Spatrick help='the matching data must be at this offset', 334061da546Spatrick default=-1) 335061da546Spatrick parser.add_option( 336061da546Spatrick '--ignore-stack', 337061da546Spatrick action='store_false', 338061da546Spatrick dest='search_stack', 339061da546Spatrick help="Don't search the stack when enumerating memory", 340061da546Spatrick default=True) 341061da546Spatrick parser.add_option( 342061da546Spatrick '--ignore-heap', 343061da546Spatrick action='store_false', 344061da546Spatrick dest='search_heap', 345061da546Spatrick help="Don't search the heap allocations when enumerating memory", 346061da546Spatrick default=True) 347061da546Spatrick parser.add_option( 348061da546Spatrick '--ignore-segments', 349061da546Spatrick action='store_false', 350061da546Spatrick dest='search_segments', 351061da546Spatrick help="Don't search readable executable segments enumerating memory", 352061da546Spatrick default=True) 353061da546Spatrick parser.add_option( 354061da546Spatrick '-V', 355061da546Spatrick '--vm-regions', 356061da546Spatrick action='store_true', 357061da546Spatrick dest='search_vm_regions', 358061da546Spatrick help='Check all VM regions instead of searching the heap, stack and segments', 359061da546Spatrick default=False) 360061da546Spatrick 361061da546Spatrick 362061da546Spatrickdef type_flags_to_string(type_flags): 363061da546Spatrick if type_flags == 0: 364061da546Spatrick type_str = 'free' 365061da546Spatrick elif type_flags & 2: 366061da546Spatrick type_str = 'malloc' 367061da546Spatrick elif type_flags & 4: 368061da546Spatrick type_str = 'free' 369061da546Spatrick elif type_flags & 1: 370061da546Spatrick type_str = 'generic' 371061da546Spatrick elif type_flags & 8: 372061da546Spatrick type_str = 'stack' 373061da546Spatrick elif type_flags & 16: 374061da546Spatrick type_str = 'stack (red zone)' 375061da546Spatrick elif type_flags & 32: 376061da546Spatrick type_str = 'segment' 377061da546Spatrick elif type_flags & 64: 378061da546Spatrick type_str = 'vm_region' 379061da546Spatrick else: 380061da546Spatrick type_str = hex(type_flags) 381061da546Spatrick return type_str 382061da546Spatrick 383061da546Spatrick 384061da546Spatrickdef find_variable_containing_address(verbose, frame, match_addr): 385061da546Spatrick variables = frame.GetVariables(True, True, True, True) 386061da546Spatrick matching_var = None 387061da546Spatrick for var in variables: 388061da546Spatrick var_addr = var.GetLoadAddress() 389061da546Spatrick if var_addr != lldb.LLDB_INVALID_ADDRESS: 390061da546Spatrick byte_size = var.GetType().GetByteSize() 391061da546Spatrick if verbose: 392061da546Spatrick print('frame #%u: [%#x - %#x) %s' % (frame.GetFrameID(), var.load_addr, var.load_addr + byte_size, var.name)) 393061da546Spatrick if var_addr == match_addr: 394061da546Spatrick if verbose: 395061da546Spatrick print('match') 396061da546Spatrick return var 397061da546Spatrick else: 398061da546Spatrick if byte_size > 0 and var_addr <= match_addr and match_addr < ( 399061da546Spatrick var_addr + byte_size): 400061da546Spatrick if verbose: 401061da546Spatrick print('match') 402061da546Spatrick return var 403061da546Spatrick return None 404061da546Spatrick 405061da546Spatrick 406061da546Spatrickdef find_frame_for_stack_address(process, addr): 407061da546Spatrick closest_delta = sys.maxsize 408061da546Spatrick closest_frame = None 409061da546Spatrick # print 'find_frame_for_stack_address(%#x)' % (addr) 410061da546Spatrick for thread in process: 411061da546Spatrick prev_sp = lldb.LLDB_INVALID_ADDRESS 412061da546Spatrick for frame in thread: 413061da546Spatrick cfa = frame.GetCFA() 414061da546Spatrick # print 'frame #%u: cfa = %#x' % (frame.GetFrameID(), cfa) 415061da546Spatrick if addr < cfa: 416061da546Spatrick delta = cfa - addr 417061da546Spatrick # print '%#x < %#x, delta = %i' % (addr, cfa, delta) 418061da546Spatrick if delta < closest_delta: 419061da546Spatrick # print 'closest' 420061da546Spatrick closest_delta = delta 421061da546Spatrick closest_frame = frame 422061da546Spatrick # else: 423061da546Spatrick # print 'delta >= closest_delta' 424061da546Spatrick return closest_frame 425061da546Spatrick 426061da546Spatrick 427061da546Spatrickdef type_flags_to_description( 428061da546Spatrick process, 429061da546Spatrick type_flags, 430061da546Spatrick ptr_addr, 431061da546Spatrick ptr_size, 432061da546Spatrick offset, 433061da546Spatrick match_addr): 434061da546Spatrick show_offset = False 435061da546Spatrick if type_flags == 0 or type_flags & 4: 436061da546Spatrick type_str = 'free(%#x)' % (ptr_addr,) 437061da546Spatrick elif type_flags & 2 or type_flags & 1: 438061da546Spatrick type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr) 439061da546Spatrick show_offset = True 440061da546Spatrick elif type_flags & 8: 441061da546Spatrick type_str = 'stack' 442061da546Spatrick frame = find_frame_for_stack_address(process, match_addr) 443061da546Spatrick if frame: 444061da546Spatrick type_str += ' in frame #%u of thread #%u: tid %#x' % (frame.GetFrameID( 445061da546Spatrick ), frame.GetThread().GetIndexID(), frame.GetThread().GetThreadID()) 446061da546Spatrick variables = frame.GetVariables(True, True, True, True) 447061da546Spatrick matching_var = None 448061da546Spatrick for var in variables: 449061da546Spatrick var_addr = var.GetLoadAddress() 450061da546Spatrick if var_addr != lldb.LLDB_INVALID_ADDRESS: 451061da546Spatrick # print 'variable "%s" @ %#x (%#x)' % (var.name, var.load_addr, 452061da546Spatrick # match_addr) 453061da546Spatrick if var_addr == match_addr: 454061da546Spatrick matching_var = var 455061da546Spatrick break 456061da546Spatrick else: 457061da546Spatrick byte_size = var.GetType().GetByteSize() 458061da546Spatrick if byte_size > 0 and var_addr <= match_addr and match_addr < ( 459061da546Spatrick var_addr + byte_size): 460061da546Spatrick matching_var = var 461061da546Spatrick break 462061da546Spatrick if matching_var: 463061da546Spatrick type_str += ' in variable at %#x:\n %s' % ( 464061da546Spatrick matching_var.GetLoadAddress(), matching_var) 465061da546Spatrick elif type_flags & 16: 466061da546Spatrick type_str = 'stack (red zone)' 467061da546Spatrick elif type_flags & 32: 468061da546Spatrick sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset) 469061da546Spatrick type_str = 'segment [%#x - %#x), %s + %u, %s' % ( 470061da546Spatrick ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) 471061da546Spatrick elif type_flags & 64: 472061da546Spatrick sb_addr = process.GetTarget().ResolveLoadAddress(ptr_addr + offset) 473061da546Spatrick type_str = 'vm_region [%#x - %#x), %s + %u, %s' % ( 474061da546Spatrick ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) 475061da546Spatrick else: 476061da546Spatrick type_str = '%#x' % (ptr_addr,) 477061da546Spatrick show_offset = True 478061da546Spatrick if show_offset and offset != 0: 479061da546Spatrick type_str += ' + %-6u' % (offset,) 480061da546Spatrick return type_str 481061da546Spatrick 482061da546Spatrick 483061da546Spatrickdef dump_stack_history_entry(options, result, stack_history_entry, idx): 484061da546Spatrick address = int(stack_history_entry.address) 485061da546Spatrick if address: 486061da546Spatrick type_flags = int(stack_history_entry.type_flags) 487061da546Spatrick symbolicator = lldb.utils.symbolication.Symbolicator() 488061da546Spatrick symbolicator.target = lldb.debugger.GetSelectedTarget() 489061da546Spatrick type_str = type_flags_to_string(type_flags) 490061da546Spatrick result.AppendMessage( 491061da546Spatrick 'stack[%u]: addr = 0x%x, type=%s, frames:' % 492061da546Spatrick (idx, address, type_str)) 493061da546Spatrick frame_idx = 0 494061da546Spatrick idx = 0 495061da546Spatrick pc = int(stack_history_entry.frames[idx]) 496061da546Spatrick while pc != 0: 497061da546Spatrick if pc >= 0x1000: 498061da546Spatrick frames = symbolicator.symbolicate(pc) 499061da546Spatrick if frames: 500061da546Spatrick for frame in frames: 501061da546Spatrick result.AppendMessage( 502061da546Spatrick ' [%u] %s' % 503061da546Spatrick (frame_idx, frame)) 504061da546Spatrick frame_idx += 1 505061da546Spatrick else: 506061da546Spatrick result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc)) 507061da546Spatrick frame_idx += 1 508061da546Spatrick idx = idx + 1 509061da546Spatrick pc = int(stack_history_entry.frames[idx]) 510061da546Spatrick else: 511061da546Spatrick pc = 0 512061da546Spatrick if idx >= options.max_frames: 513061da546Spatrick result.AppendMessage( 514061da546Spatrick 'warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' % 515061da546Spatrick (options.max_frames)) 516061da546Spatrick 517061da546Spatrick result.AppendMessage('') 518061da546Spatrick 519061da546Spatrick 520061da546Spatrickdef dump_stack_history_entries(options, result, addr, history): 521061da546Spatrick # malloc_stack_entry *get_stack_history_for_address (const void * addr) 522061da546Spatrick expr_prefix = ''' 523061da546Spatricktypedef int kern_return_t; 524061da546Spatricktypedef struct $malloc_stack_entry { 525061da546Spatrick uint64_t address; 526061da546Spatrick uint64_t argument; 527061da546Spatrick uint32_t type_flags; 528061da546Spatrick uint32_t num_frames; 529061da546Spatrick uint64_t frames[512]; 530061da546Spatrick kern_return_t err; 531061da546Spatrick} $malloc_stack_entry; 532061da546Spatrick''' 533061da546Spatrick single_expr = ''' 534061da546Spatrick#define MAX_FRAMES %u 535061da546Spatricktypedef unsigned task_t; 536061da546Spatrick$malloc_stack_entry stack; 537061da546Spatrickstack.address = 0x%x; 538061da546Spatrickstack.type_flags = 2; 539061da546Spatrickstack.num_frames = 0; 540061da546Spatrickstack.frames[0] = 0; 541061da546Spatrickuint32_t max_stack_frames = MAX_FRAMES; 542061da546Spatrickstack.err = (kern_return_t)__mach_stack_logging_get_frames ( 543061da546Spatrick (task_t)mach_task_self(), 544061da546Spatrick stack.address, 545061da546Spatrick &stack.frames[0], 546061da546Spatrick max_stack_frames, 547061da546Spatrick &stack.num_frames); 548061da546Spatrickif (stack.num_frames < MAX_FRAMES) 549061da546Spatrick stack.frames[stack.num_frames] = 0; 550061da546Spatrickelse 551061da546Spatrick stack.frames[MAX_FRAMES-1] = 0; 552061da546Spatrickstack''' % (options.max_frames, addr) 553061da546Spatrick 554061da546Spatrick history_expr = ''' 555061da546Spatricktypedef int kern_return_t; 556061da546Spatricktypedef unsigned task_t; 557061da546Spatrick#define MAX_FRAMES %u 558061da546Spatrick#define MAX_HISTORY %u 559061da546Spatricktypedef struct mach_stack_logging_record_t { 560061da546Spatrick uint32_t type_flags; 561061da546Spatrick uint64_t stack_identifier; 562061da546Spatrick uint64_t argument; 563061da546Spatrick uint64_t address; 564061da546Spatrick} mach_stack_logging_record_t; 565061da546Spatricktypedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *); 566061da546Spatricktypedef struct malloc_stack_entry { 567061da546Spatrick uint64_t address; 568061da546Spatrick uint64_t argument; 569061da546Spatrick uint32_t type_flags; 570061da546Spatrick uint32_t num_frames; 571061da546Spatrick uint64_t frames[MAX_FRAMES]; 572061da546Spatrick kern_return_t frames_err; 573061da546Spatrick} malloc_stack_entry; 574061da546Spatricktypedef struct $malloc_stack_history { 575061da546Spatrick task_t task; 576061da546Spatrick unsigned idx; 577061da546Spatrick malloc_stack_entry entries[MAX_HISTORY]; 578061da546Spatrick} $malloc_stack_history; 579061da546Spatrick$malloc_stack_history lldb_info = { (task_t)mach_task_self(), 0 }; 580061da546Spatrickuint32_t max_stack_frames = MAX_FRAMES; 581061da546Spatrickenumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void { 582061da546Spatrick $malloc_stack_history *lldb_info = ($malloc_stack_history *)baton; 583061da546Spatrick if (lldb_info->idx < MAX_HISTORY) { 584061da546Spatrick malloc_stack_entry *stack_entry = &(lldb_info->entries[lldb_info->idx]); 585061da546Spatrick stack_entry->address = stack_record.address; 586061da546Spatrick stack_entry->type_flags = stack_record.type_flags; 587061da546Spatrick stack_entry->argument = stack_record.argument; 588061da546Spatrick stack_entry->num_frames = 0; 589061da546Spatrick stack_entry->frames[0] = 0; 590061da546Spatrick stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack ( 591061da546Spatrick lldb_info->task, 592061da546Spatrick stack_record.stack_identifier, 593061da546Spatrick stack_entry->frames, 594061da546Spatrick (uint32_t)MAX_FRAMES, 595061da546Spatrick &stack_entry->num_frames); 596061da546Spatrick // Terminate the frames with zero if there is room 597061da546Spatrick if (stack_entry->num_frames < MAX_FRAMES) 598061da546Spatrick stack_entry->frames[stack_entry->num_frames] = 0; 599061da546Spatrick } 600061da546Spatrick ++lldb_info->idx; 601061da546Spatrick}; 602061da546Spatrick(kern_return_t)__mach_stack_logging_enumerate_records (lldb_info.task, (uint64_t)0x%x, callback, &lldb_info); 603061da546Spatricklldb_info''' % (options.max_frames, options.max_history, addr) 604061da546Spatrick 605061da546Spatrick frame = lldb.debugger.GetSelectedTarget().GetProcess( 606061da546Spatrick ).GetSelectedThread().GetSelectedFrame() 607061da546Spatrick if history: 608061da546Spatrick expr = history_expr 609061da546Spatrick else: 610061da546Spatrick expr = single_expr 611061da546Spatrick expr_options = lldb.SBExpressionOptions() 612061da546Spatrick expr_options.SetIgnoreBreakpoints(True) 613061da546Spatrick expr_options.SetTimeoutInMicroSeconds(5 * 1000 * 1000) # 5 second timeout 614061da546Spatrick expr_options.SetTryAllThreads(True) 615061da546Spatrick expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) 616061da546Spatrick expr_options.SetPrefix(expr_prefix) 617061da546Spatrick expr_sbvalue = frame.EvaluateExpression(expr, expr_options) 618061da546Spatrick if options.verbose: 619061da546Spatrick print("expression:") 620061da546Spatrick print(expr) 621061da546Spatrick print("expression result:") 622061da546Spatrick print(expr_sbvalue) 623061da546Spatrick if expr_sbvalue.error.Success(): 624061da546Spatrick if history: 625061da546Spatrick malloc_stack_history = lldb.value(expr_sbvalue) 626061da546Spatrick num_stacks = int(malloc_stack_history.idx) 627061da546Spatrick if num_stacks <= options.max_history: 628061da546Spatrick i_max = num_stacks 629061da546Spatrick else: 630061da546Spatrick i_max = options.max_history 631061da546Spatrick for i in range(i_max): 632061da546Spatrick stack_history_entry = malloc_stack_history.entries[i] 633061da546Spatrick dump_stack_history_entry( 634061da546Spatrick options, result, stack_history_entry, i) 635061da546Spatrick if num_stacks > options.max_history: 636061da546Spatrick result.AppendMessage( 637061da546Spatrick 'warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' % 638061da546Spatrick (options.max_history, num_stacks)) 639061da546Spatrick else: 640061da546Spatrick stack_history_entry = lldb.value(expr_sbvalue) 641061da546Spatrick dump_stack_history_entry(options, result, stack_history_entry, 0) 642061da546Spatrick 643061da546Spatrick else: 644061da546Spatrick result.AppendMessage( 645061da546Spatrick 'error: expression failed "%s" => %s' % 646061da546Spatrick (expr, expr_sbvalue.error)) 647061da546Spatrick 648061da546Spatrick 649061da546Spatrickdef display_match_results( 650061da546Spatrick process, 651061da546Spatrick result, 652061da546Spatrick options, 653061da546Spatrick arg_str_description, 654061da546Spatrick expr, 655061da546Spatrick print_no_matches, 656061da546Spatrick expr_prefix=None): 657061da546Spatrick frame = lldb.debugger.GetSelectedTarget().GetProcess( 658061da546Spatrick ).GetSelectedThread().GetSelectedFrame() 659061da546Spatrick if not frame: 660061da546Spatrick result.AppendMessage('error: invalid frame') 661061da546Spatrick return 0 662061da546Spatrick expr_options = lldb.SBExpressionOptions() 663061da546Spatrick expr_options.SetIgnoreBreakpoints(True) 664061da546Spatrick expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues) 665061da546Spatrick expr_options.SetTimeoutInMicroSeconds( 666061da546Spatrick 30 * 1000 * 1000) # 30 second timeout 667061da546Spatrick expr_options.SetTryAllThreads(False) 668061da546Spatrick expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) 669061da546Spatrick if expr_prefix: 670061da546Spatrick expr_options.SetPrefix(expr_prefix) 671061da546Spatrick expr_sbvalue = frame.EvaluateExpression(expr, expr_options) 672061da546Spatrick if options.verbose: 673061da546Spatrick print("expression:") 674061da546Spatrick print(expr) 675061da546Spatrick print("expression result:") 676061da546Spatrick print(expr_sbvalue) 677061da546Spatrick if expr_sbvalue.error.Success(): 678061da546Spatrick match_value = lldb.value(expr_sbvalue) 679061da546Spatrick i = 0 680061da546Spatrick match_idx = 0 681061da546Spatrick while True: 682061da546Spatrick print_entry = True 683061da546Spatrick match_entry = match_value[i] 684061da546Spatrick i += 1 685061da546Spatrick if i > options.max_matches: 686061da546Spatrick result.AppendMessage( 687061da546Spatrick 'warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % 688061da546Spatrick (options.max_matches)) 689061da546Spatrick break 690061da546Spatrick malloc_addr = match_entry.addr.sbvalue.unsigned 691061da546Spatrick if malloc_addr == 0: 692061da546Spatrick break 693061da546Spatrick malloc_size = int(match_entry.size) 694061da546Spatrick offset = int(match_entry.offset) 695061da546Spatrick 696061da546Spatrick if options.offset >= 0 and options.offset != offset: 697061da546Spatrick print_entry = False 698061da546Spatrick else: 699061da546Spatrick match_addr = malloc_addr + offset 700061da546Spatrick type_flags = int(match_entry.type) 701061da546Spatrick #result.AppendMessage (hex(malloc_addr + offset)) 702061da546Spatrick if type_flags == 64: 703061da546Spatrick search_stack_old = options.search_stack 704061da546Spatrick search_segments_old = options.search_segments 705061da546Spatrick search_heap_old = options.search_heap 706061da546Spatrick search_vm_regions = options.search_vm_regions 707061da546Spatrick options.search_stack = True 708061da546Spatrick options.search_segments = True 709061da546Spatrick options.search_heap = True 710061da546Spatrick options.search_vm_regions = False 711061da546Spatrick if malloc_info_impl(lldb.debugger, result, options, [ 712061da546Spatrick hex(malloc_addr + offset)]): 713061da546Spatrick print_entry = False 714061da546Spatrick options.search_stack = search_stack_old 715061da546Spatrick options.search_segments = search_segments_old 716061da546Spatrick options.search_heap = search_heap_old 717061da546Spatrick options.search_vm_regions = search_vm_regions 718061da546Spatrick if print_entry: 719061da546Spatrick description = '%#16.16x: %s' % (match_addr, type_flags_to_description( 720061da546Spatrick process, type_flags, malloc_addr, malloc_size, offset, match_addr)) 721061da546Spatrick if options.show_size: 722061da546Spatrick description += ' <%5u>' % (malloc_size) 723061da546Spatrick if options.show_range: 724061da546Spatrick description += ' [%#x - %#x)' % ( 725061da546Spatrick malloc_addr, malloc_addr + malloc_size) 726061da546Spatrick derefed_dynamic_value = None 727061da546Spatrick dynamic_value = match_entry.addr.sbvalue.GetDynamicValue( 728061da546Spatrick lldb.eDynamicCanRunTarget) 729061da546Spatrick if dynamic_value.type.name == 'void *': 730061da546Spatrick if options.type == 'pointer' and malloc_size == 4096: 731061da546Spatrick error = lldb.SBError() 732061da546Spatrick process = expr_sbvalue.GetProcess() 733061da546Spatrick target = expr_sbvalue.GetTarget() 734061da546Spatrick data = bytearray( 735061da546Spatrick process.ReadMemory( 736061da546Spatrick malloc_addr, 16, error)) 737061da546Spatrick if data == '\xa1\xa1\xa1\xa1AUTORELEASE!': 738061da546Spatrick ptr_size = target.addr_size 739061da546Spatrick thread = process.ReadUnsignedFromMemory( 740061da546Spatrick malloc_addr + 16 + ptr_size, ptr_size, error) 741061da546Spatrick # 4 bytes 0xa1a1a1a1 742061da546Spatrick # 12 bytes 'AUTORELEASE!' 743061da546Spatrick # ptr bytes autorelease insertion point 744061da546Spatrick # ptr bytes pthread_t 745061da546Spatrick # ptr bytes next colder page 746061da546Spatrick # ptr bytes next hotter page 747061da546Spatrick # 4 bytes this page's depth in the list 748061da546Spatrick # 4 bytes high-water mark 749061da546Spatrick description += ' AUTORELEASE! for pthread_t %#x' % ( 750061da546Spatrick thread) 751061da546Spatrick # else: 752061da546Spatrick # description += 'malloc(%u)' % (malloc_size) 753061da546Spatrick # else: 754061da546Spatrick # description += 'malloc(%u)' % (malloc_size) 755061da546Spatrick else: 756061da546Spatrick derefed_dynamic_value = dynamic_value.deref 757061da546Spatrick if derefed_dynamic_value: 758061da546Spatrick derefed_dynamic_type = derefed_dynamic_value.type 759061da546Spatrick derefed_dynamic_type_size = derefed_dynamic_type.size 760061da546Spatrick derefed_dynamic_type_name = derefed_dynamic_type.name 761061da546Spatrick description += ' ' 762061da546Spatrick description += derefed_dynamic_type_name 763061da546Spatrick if offset < derefed_dynamic_type_size: 764061da546Spatrick member_list = list() 765061da546Spatrick get_member_types_for_offset( 766061da546Spatrick derefed_dynamic_type, offset, member_list) 767061da546Spatrick if member_list: 768061da546Spatrick member_path = '' 769061da546Spatrick for member in member_list: 770061da546Spatrick member_name = member.name 771061da546Spatrick if member_name: 772061da546Spatrick if member_path: 773061da546Spatrick member_path += '.' 774061da546Spatrick member_path += member_name 775061da546Spatrick if member_path: 776dda28197Spatrick if options.ivar_regex_exclusions: 777dda28197Spatrick for ivar_regex in options.ivar_regex_exclusions: 778061da546Spatrick if ivar_regex.match( 779061da546Spatrick member_path): 780061da546Spatrick print_entry = False 781061da546Spatrick description += '.%s' % (member_path) 782061da546Spatrick else: 783061da546Spatrick description += '%u bytes after %s' % ( 784061da546Spatrick offset - derefed_dynamic_type_size, derefed_dynamic_type_name) 785061da546Spatrick else: 786061da546Spatrick # strip the "*" from the end of the name since we 787061da546Spatrick # were unable to dereference this 788061da546Spatrick description += dynamic_value.type.name[0:-1] 789061da546Spatrick if print_entry: 790061da546Spatrick match_idx += 1 791061da546Spatrick result_output = '' 792061da546Spatrick if description: 793061da546Spatrick result_output += description 794061da546Spatrick if options.print_type and derefed_dynamic_value: 795061da546Spatrick result_output += ' %s' % (derefed_dynamic_value) 796061da546Spatrick if options.print_object_description and dynamic_value: 797061da546Spatrick desc = dynamic_value.GetObjectDescription() 798061da546Spatrick if desc: 799061da546Spatrick result_output += '\n%s' % (desc) 800061da546Spatrick if result_output: 801061da546Spatrick result.AppendMessage(result_output) 802061da546Spatrick if options.memory: 803061da546Spatrick cmd_result = lldb.SBCommandReturnObject() 804061da546Spatrick if options.format is None: 805061da546Spatrick memory_command = "memory read --force 0x%x 0x%x" % ( 806061da546Spatrick malloc_addr, malloc_addr + malloc_size) 807061da546Spatrick else: 808061da546Spatrick memory_command = "memory read --force -f %s 0x%x 0x%x" % ( 809061da546Spatrick options.format, malloc_addr, malloc_addr + malloc_size) 810061da546Spatrick if options.verbose: 811061da546Spatrick result.AppendMessage(memory_command) 812061da546Spatrick lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result) 813061da546Spatrick result.AppendMessage(cmd_result.GetOutput()) 814061da546Spatrick if options.stack_history: 815061da546Spatrick dump_stack_history_entries(options, result, malloc_addr, 1) 816061da546Spatrick elif options.stack: 817061da546Spatrick dump_stack_history_entries(options, result, malloc_addr, 0) 818061da546Spatrick return i 819061da546Spatrick else: 820061da546Spatrick result.AppendMessage(str(expr_sbvalue.error)) 821061da546Spatrick return 0 822061da546Spatrick 823061da546Spatrick 824061da546Spatrickdef get_ptr_refs_options(): 825061da546Spatrick usage = "usage: %prog [options] <EXPR> [EXPR ...]" 826061da546Spatrick description = '''Searches all allocations on the heap for pointer values on 827061da546Spatrickdarwin user space programs. Any matches that were found will dump the malloc 828061da546Spatrickblocks that contain the pointers and might be able to print what kind of 829061da546Spatrickobjects the pointers are contained in using dynamic type information in the 830061da546Spatrickprogram.''' 831061da546Spatrick parser = optparse.OptionParser( 832061da546Spatrick description=description, 833061da546Spatrick prog='ptr_refs', 834061da546Spatrick usage=usage) 835061da546Spatrick add_common_options(parser) 836061da546Spatrick return parser 837061da546Spatrick 838061da546Spatrick 839061da546Spatrickdef find_variable(debugger, command, result, dict): 840061da546Spatrick usage = "usage: %prog [options] <ADDR> [ADDR ...]" 841061da546Spatrick description = '''Searches for a local variable in all frames that contains a hex ADDR.''' 842061da546Spatrick command_args = shlex.split(command) 843061da546Spatrick parser = optparse.OptionParser( 844061da546Spatrick description=description, 845061da546Spatrick prog='find_variable', 846061da546Spatrick usage=usage) 847061da546Spatrick parser.add_option( 848061da546Spatrick '-v', 849061da546Spatrick '--verbose', 850061da546Spatrick action='store_true', 851061da546Spatrick dest='verbose', 852061da546Spatrick help='display verbose debug info', 853061da546Spatrick default=False) 854061da546Spatrick try: 855061da546Spatrick (options, args) = parser.parse_args(command_args) 856061da546Spatrick except: 857061da546Spatrick return 858061da546Spatrick 859061da546Spatrick process = debugger.GetSelectedTarget().GetProcess() 860061da546Spatrick if not process: 861061da546Spatrick result.AppendMessage('error: invalid process') 862061da546Spatrick return 863061da546Spatrick 864061da546Spatrick for arg in args: 865061da546Spatrick var_addr = int(arg, 16) 866061da546Spatrick print("Finding a variable with address %#x..." % (var_addr), file=result) 867061da546Spatrick done = False 868061da546Spatrick for thread in process: 869061da546Spatrick for frame in thread: 870061da546Spatrick var = find_variable_containing_address( 871061da546Spatrick options.verbose, frame, var_addr) 872061da546Spatrick if var: 873061da546Spatrick print(var) 874061da546Spatrick done = True 875061da546Spatrick break 876061da546Spatrick if done: 877061da546Spatrick break 878061da546Spatrick 879061da546Spatrick 880061da546Spatrickdef ptr_refs(debugger, command, result, dict): 881061da546Spatrick command_args = shlex.split(command) 882061da546Spatrick parser = get_ptr_refs_options() 883061da546Spatrick try: 884061da546Spatrick (options, args) = parser.parse_args(command_args) 885061da546Spatrick except: 886061da546Spatrick return 887061da546Spatrick 888061da546Spatrick process = debugger.GetSelectedTarget().GetProcess() 889061da546Spatrick if not process: 890061da546Spatrick result.AppendMessage('error: invalid process') 891061da546Spatrick return 892061da546Spatrick frame = process.GetSelectedThread().GetSelectedFrame() 893061da546Spatrick if not frame: 894061da546Spatrick result.AppendMessage('error: invalid frame') 895061da546Spatrick return 896061da546Spatrick 897061da546Spatrick options.type = 'pointer' 898061da546Spatrick if options.format is None: 899061da546Spatrick options.format = "A" # 'A' is "address" format 900061da546Spatrick 901061da546Spatrick if args: 902061da546Spatrick # When we initialize the expression, we must define any types that 903061da546Spatrick # we will need when looking at every allocation. We must also define 904061da546Spatrick # a type named callback_baton_t and make an instance named "baton" 905061da546Spatrick # and initialize it how ever we want to. The address of "baton" will 906061da546Spatrick # be passed into our range callback. callback_baton_t must contain 907061da546Spatrick # a member named "callback" whose type is "range_callback_t". This 908061da546Spatrick # will be used by our zone callbacks to call the range callback for 909061da546Spatrick # each malloc range. 910061da546Spatrick expr_prefix = ''' 911061da546Spatrickstruct $malloc_match { 912061da546Spatrick void *addr; 913061da546Spatrick uintptr_t size; 914061da546Spatrick uintptr_t offset; 915061da546Spatrick uintptr_t type; 916061da546Spatrick}; 917061da546Spatrick''' 918061da546Spatrick user_init_code_format = ''' 919061da546Spatrick#define MAX_MATCHES %u 920061da546Spatricktypedef struct callback_baton_t { 921061da546Spatrick range_callback_t callback; 922061da546Spatrick unsigned num_matches; 923061da546Spatrick $malloc_match matches[MAX_MATCHES]; 924061da546Spatrick void *ptr; 925061da546Spatrick} callback_baton_t; 926061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 927061da546Spatrick callback_baton_t *lldb_info = (callback_baton_t *)baton; 928061da546Spatrick typedef void* T; 929061da546Spatrick const unsigned size = sizeof(T); 930061da546Spatrick T *array = (T*)ptr_addr; 931061da546Spatrick for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) { 932061da546Spatrick if (array[idx] == lldb_info->ptr) { 933061da546Spatrick if (lldb_info->num_matches < MAX_MATCHES) { 934061da546Spatrick lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr; 935061da546Spatrick lldb_info->matches[lldb_info->num_matches].size = ptr_size; 936061da546Spatrick lldb_info->matches[lldb_info->num_matches].offset = idx*sizeof(T); 937061da546Spatrick lldb_info->matches[lldb_info->num_matches].type = type; 938061da546Spatrick ++lldb_info->num_matches; 939061da546Spatrick } 940061da546Spatrick } 941061da546Spatrick } 942061da546Spatrick}; 943061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; 944061da546Spatrick''' 945061da546Spatrick # We must also define a snippet of code to be run that returns 946061da546Spatrick # the result of the expression we run. 947061da546Spatrick # Here we return NULL if our pointer was not found in any malloc blocks, 948061da546Spatrick # and we return the address of the matches array so we can then access 949061da546Spatrick # the matching results 950061da546Spatrick user_return_code = '''if (baton.num_matches < MAX_MATCHES) 951061da546Spatrick baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 952061da546Spatrickbaton.matches''' 953061da546Spatrick # Iterate through all of our pointer expressions and display the 954061da546Spatrick # results 955061da546Spatrick for ptr_expr in args: 956061da546Spatrick user_init_code = user_init_code_format % ( 957061da546Spatrick options.max_matches, ptr_expr) 958061da546Spatrick expr = get_iterate_memory_expr( 959061da546Spatrick options, process, user_init_code, user_return_code) 960061da546Spatrick arg_str_description = 'malloc block containing pointer %s' % ptr_expr 961061da546Spatrick display_match_results( 962061da546Spatrick process, 963061da546Spatrick result, 964061da546Spatrick options, 965061da546Spatrick arg_str_description, 966061da546Spatrick expr, 967061da546Spatrick True, 968061da546Spatrick expr_prefix) 969061da546Spatrick else: 970061da546Spatrick result.AppendMessage('error: no pointer arguments were given') 971061da546Spatrick 972061da546Spatrick 973061da546Spatrickdef get_cstr_refs_options(): 974061da546Spatrick usage = "usage: %prog [options] <CSTR> [CSTR ...]" 975061da546Spatrick description = '''Searches all allocations on the heap for C string values on 976061da546Spatrickdarwin user space programs. Any matches that were found will dump the malloc 977061da546Spatrickblocks that contain the C strings and might be able to print what kind of 978061da546Spatrickobjects the pointers are contained in using dynamic type information in the 979061da546Spatrickprogram.''' 980061da546Spatrick parser = optparse.OptionParser( 981061da546Spatrick description=description, 982061da546Spatrick prog='cstr_refs', 983061da546Spatrick usage=usage) 984061da546Spatrick add_common_options(parser) 985061da546Spatrick return parser 986061da546Spatrick 987061da546Spatrick 988061da546Spatrickdef cstr_refs(debugger, command, result, dict): 989061da546Spatrick command_args = shlex.split(command) 990061da546Spatrick parser = get_cstr_refs_options() 991061da546Spatrick try: 992061da546Spatrick (options, args) = parser.parse_args(command_args) 993061da546Spatrick except: 994061da546Spatrick return 995061da546Spatrick 996061da546Spatrick process = debugger.GetSelectedTarget().GetProcess() 997061da546Spatrick if not process: 998061da546Spatrick result.AppendMessage('error: invalid process') 999061da546Spatrick return 1000061da546Spatrick frame = process.GetSelectedThread().GetSelectedFrame() 1001061da546Spatrick if not frame: 1002061da546Spatrick result.AppendMessage('error: invalid frame') 1003061da546Spatrick return 1004061da546Spatrick 1005061da546Spatrick options.type = 'cstr' 1006061da546Spatrick if options.format is None: 1007061da546Spatrick options.format = "Y" # 'Y' is "bytes with ASCII" format 1008061da546Spatrick 1009061da546Spatrick if args: 1010061da546Spatrick # When we initialize the expression, we must define any types that 1011061da546Spatrick # we will need when looking at every allocation. We must also define 1012061da546Spatrick # a type named callback_baton_t and make an instance named "baton" 1013061da546Spatrick # and initialize it how ever we want to. The address of "baton" will 1014061da546Spatrick # be passed into our range callback. callback_baton_t must contain 1015061da546Spatrick # a member named "callback" whose type is "range_callback_t". This 1016061da546Spatrick # will be used by our zone callbacks to call the range callback for 1017061da546Spatrick # each malloc range. 1018061da546Spatrick expr_prefix = ''' 1019061da546Spatrickstruct $malloc_match { 1020061da546Spatrick void *addr; 1021061da546Spatrick uintptr_t size; 1022061da546Spatrick uintptr_t offset; 1023061da546Spatrick uintptr_t type; 1024061da546Spatrick}; 1025061da546Spatrick''' 1026061da546Spatrick user_init_code_format = ''' 1027061da546Spatrick#define MAX_MATCHES %u 1028061da546Spatricktypedef struct callback_baton_t { 1029061da546Spatrick range_callback_t callback; 1030061da546Spatrick unsigned num_matches; 1031061da546Spatrick $malloc_match matches[MAX_MATCHES]; 1032061da546Spatrick const char *cstr; 1033061da546Spatrick unsigned cstr_len; 1034061da546Spatrick} callback_baton_t; 1035061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 1036061da546Spatrick callback_baton_t *lldb_info = (callback_baton_t *)baton; 1037061da546Spatrick if (lldb_info->cstr_len < ptr_size) { 1038061da546Spatrick const char *begin = (const char *)ptr_addr; 1039061da546Spatrick const char *end = begin + ptr_size - lldb_info->cstr_len; 1040061da546Spatrick for (const char *s = begin; s < end; ++s) { 1041061da546Spatrick if ((int)memcmp(s, lldb_info->cstr, lldb_info->cstr_len) == 0) { 1042061da546Spatrick if (lldb_info->num_matches < MAX_MATCHES) { 1043061da546Spatrick lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr; 1044061da546Spatrick lldb_info->matches[lldb_info->num_matches].size = ptr_size; 1045061da546Spatrick lldb_info->matches[lldb_info->num_matches].offset = s - begin; 1046061da546Spatrick lldb_info->matches[lldb_info->num_matches].type = type; 1047061da546Spatrick ++lldb_info->num_matches; 1048061da546Spatrick } 1049061da546Spatrick } 1050061da546Spatrick } 1051061da546Spatrick } 1052061da546Spatrick}; 1053061da546Spatrickconst char *cstr = "%s"; 1054061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };''' 1055061da546Spatrick # We must also define a snippet of code to be run that returns 1056061da546Spatrick # the result of the expression we run. 1057061da546Spatrick # Here we return NULL if our pointer was not found in any malloc blocks, 1058061da546Spatrick # and we return the address of the matches array so we can then access 1059061da546Spatrick # the matching results 1060061da546Spatrick user_return_code = '''if (baton.num_matches < MAX_MATCHES) 1061061da546Spatrick baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 1062061da546Spatrickbaton.matches''' 1063061da546Spatrick # Iterate through all of our pointer expressions and display the 1064061da546Spatrick # results 1065061da546Spatrick for cstr in args: 1066061da546Spatrick user_init_code = user_init_code_format % ( 1067061da546Spatrick options.max_matches, cstr) 1068061da546Spatrick expr = get_iterate_memory_expr( 1069061da546Spatrick options, process, user_init_code, user_return_code) 1070061da546Spatrick arg_str_description = 'malloc block containing "%s"' % cstr 1071061da546Spatrick display_match_results( 1072061da546Spatrick process, 1073061da546Spatrick result, 1074061da546Spatrick options, 1075061da546Spatrick arg_str_description, 1076061da546Spatrick expr, 1077061da546Spatrick True, 1078061da546Spatrick expr_prefix) 1079061da546Spatrick else: 1080061da546Spatrick result.AppendMessage( 1081061da546Spatrick 'error: command takes one or more C string arguments') 1082061da546Spatrick 1083061da546Spatrick 1084061da546Spatrickdef get_malloc_info_options(): 1085061da546Spatrick usage = "usage: %prog [options] <EXPR> [EXPR ...]" 1086061da546Spatrick description = '''Searches the heap a malloc block that contains the addresses 1087061da546Spatrickspecified as one or more address expressions. Any matches that were found will 1088061da546Spatrickdump the malloc blocks that match or contain the specified address. The matching 1089061da546Spatrickblocks might be able to show what kind of objects they are using dynamic type 1090061da546Spatrickinformation in the program.''' 1091061da546Spatrick parser = optparse.OptionParser( 1092061da546Spatrick description=description, 1093061da546Spatrick prog='malloc_info', 1094061da546Spatrick usage=usage) 1095061da546Spatrick add_common_options(parser) 1096061da546Spatrick return parser 1097061da546Spatrick 1098061da546Spatrick 1099061da546Spatrickdef malloc_info(debugger, command, result, dict): 1100061da546Spatrick command_args = shlex.split(command) 1101061da546Spatrick parser = get_malloc_info_options() 1102061da546Spatrick try: 1103061da546Spatrick (options, args) = parser.parse_args(command_args) 1104061da546Spatrick except: 1105061da546Spatrick return 1106061da546Spatrick malloc_info_impl(debugger, result, options, args) 1107061da546Spatrick 1108061da546Spatrick 1109061da546Spatrickdef malloc_info_impl(debugger, result, options, args): 1110061da546Spatrick # We are specifically looking for something on the heap only 1111061da546Spatrick options.type = 'malloc_info' 1112061da546Spatrick 1113061da546Spatrick process = debugger.GetSelectedTarget().GetProcess() 1114061da546Spatrick if not process: 1115061da546Spatrick result.AppendMessage('error: invalid process') 1116061da546Spatrick return 1117061da546Spatrick frame = process.GetSelectedThread().GetSelectedFrame() 1118061da546Spatrick if not frame: 1119061da546Spatrick result.AppendMessage('error: invalid frame') 1120061da546Spatrick return 1121061da546Spatrick expr_prefix = ''' 1122061da546Spatrickstruct $malloc_match { 1123061da546Spatrick void *addr; 1124061da546Spatrick uintptr_t size; 1125061da546Spatrick uintptr_t offset; 1126061da546Spatrick uintptr_t type; 1127061da546Spatrick}; 1128061da546Spatrick''' 1129061da546Spatrick 1130061da546Spatrick user_init_code_format = ''' 1131061da546Spatricktypedef struct callback_baton_t { 1132061da546Spatrick range_callback_t callback; 1133061da546Spatrick unsigned num_matches; 1134061da546Spatrick $malloc_match matches[2]; // Two items so they can be NULL terminated 1135061da546Spatrick void *ptr; 1136061da546Spatrick} callback_baton_t; 1137061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 1138061da546Spatrick callback_baton_t *lldb_info = (callback_baton_t *)baton; 1139061da546Spatrick if (lldb_info->num_matches == 0) { 1140061da546Spatrick uint8_t *p = (uint8_t *)lldb_info->ptr; 1141061da546Spatrick uint8_t *lo = (uint8_t *)ptr_addr; 1142061da546Spatrick uint8_t *hi = lo + ptr_size; 1143061da546Spatrick if (lo <= p && p < hi) { 1144061da546Spatrick lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr; 1145061da546Spatrick lldb_info->matches[lldb_info->num_matches].size = ptr_size; 1146061da546Spatrick lldb_info->matches[lldb_info->num_matches].offset = p - lo; 1147061da546Spatrick lldb_info->matches[lldb_info->num_matches].type = type; 1148061da546Spatrick lldb_info->num_matches = 1; 1149061da546Spatrick } 1150061da546Spatrick } 1151061da546Spatrick}; 1152061da546Spatrickcallback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; 1153061da546Spatrickbaton.matches[0].addr = 0; 1154061da546Spatrickbaton.matches[1].addr = 0;''' 1155061da546Spatrick if args: 1156061da546Spatrick total_matches = 0 1157061da546Spatrick for ptr_expr in args: 1158061da546Spatrick user_init_code = user_init_code_format % (ptr_expr) 1159061da546Spatrick expr = get_iterate_memory_expr( 1160061da546Spatrick options, process, user_init_code, 'baton.matches') 1161061da546Spatrick arg_str_description = 'malloc block that contains %s' % ptr_expr 1162061da546Spatrick total_matches += display_match_results( 1163061da546Spatrick process, result, options, arg_str_description, expr, True, expr_prefix) 1164061da546Spatrick return total_matches 1165061da546Spatrick else: 1166061da546Spatrick result.AppendMessage( 1167061da546Spatrick 'error: command takes one or more pointer expressions') 1168061da546Spatrick return 0 1169061da546Spatrick 1170061da546Spatrick 1171061da546Spatrickdef get_thread_stack_ranges_struct(process): 1172061da546Spatrick '''Create code that defines a structure that represents threads stack bounds 1173061da546Spatrick for all threads. It returns a static sized array initialized with all of 1174061da546Spatrick the tid, base, size structs for all the threads.''' 1175061da546Spatrick stack_dicts = list() 1176061da546Spatrick if process: 1177061da546Spatrick i = 0 1178061da546Spatrick for thread in process: 1179061da546Spatrick min_sp = thread.frame[0].sp 1180061da546Spatrick max_sp = min_sp 1181061da546Spatrick for frame in thread.frames: 1182061da546Spatrick sp = frame.sp 1183061da546Spatrick if sp < min_sp: 1184061da546Spatrick min_sp = sp 1185061da546Spatrick if sp > max_sp: 1186061da546Spatrick max_sp = sp 1187061da546Spatrick if min_sp < max_sp: 1188061da546Spatrick stack_dicts.append({'tid': thread.GetThreadID( 1189061da546Spatrick ), 'base': min_sp, 'size': max_sp - min_sp, 'index': i}) 1190061da546Spatrick i += 1 1191061da546Spatrick stack_dicts_len = len(stack_dicts) 1192061da546Spatrick if stack_dicts_len > 0: 1193061da546Spatrick result = ''' 1194061da546Spatrick#define NUM_STACKS %u 1195061da546Spatrick#define STACK_RED_ZONE_SIZE %u 1196061da546Spatricktypedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t; 1197061da546Spatrickthread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize()) 1198061da546Spatrick for stack_dict in stack_dicts: 1199061da546Spatrick result += ''' 1200061da546Spatrickstacks[%(index)u].tid = 0x%(tid)x; 1201061da546Spatrickstacks[%(index)u].base = 0x%(base)x; 1202061da546Spatrickstacks[%(index)u].size = 0x%(size)x;''' % stack_dict 1203061da546Spatrick return result 1204061da546Spatrick else: 1205061da546Spatrick return '' 1206061da546Spatrick 1207061da546Spatrick 1208061da546Spatrickdef get_sections_ranges_struct(process): 1209061da546Spatrick '''Create code that defines a structure that represents all segments that 1210061da546Spatrick can contain data for all images in "target". It returns a static sized 1211061da546Spatrick array initialized with all of base, size structs for all the threads.''' 1212061da546Spatrick target = process.target 1213061da546Spatrick segment_dicts = list() 1214061da546Spatrick for (module_idx, module) in enumerate(target.modules): 1215061da546Spatrick for sect_idx in range(module.GetNumSections()): 1216061da546Spatrick section = module.GetSectionAtIndex(sect_idx) 1217061da546Spatrick if not section: 1218061da546Spatrick break 1219061da546Spatrick name = section.name 1220061da546Spatrick if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO': 1221061da546Spatrick base = section.GetLoadAddress(target) 1222061da546Spatrick size = section.GetByteSize() 1223061da546Spatrick if base != lldb.LLDB_INVALID_ADDRESS and size > 0: 1224061da546Spatrick segment_dicts.append({'base': base, 'size': size}) 1225061da546Spatrick segment_dicts_len = len(segment_dicts) 1226061da546Spatrick if segment_dicts_len > 0: 1227061da546Spatrick result = ''' 1228061da546Spatrick#define NUM_SEGMENTS %u 1229061da546Spatricktypedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t; 1230061da546Spatricksegment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,) 1231061da546Spatrick for (idx, segment_dict) in enumerate(segment_dicts): 1232061da546Spatrick segment_dict['index'] = idx 1233061da546Spatrick result += ''' 1234061da546Spatricksegments[%(index)u].base = 0x%(base)x; 1235061da546Spatricksegments[%(index)u].size = 0x%(size)x;''' % segment_dict 1236061da546Spatrick return result 1237061da546Spatrick else: 1238061da546Spatrick return '' 1239061da546Spatrick 1240061da546Spatrick 1241061da546Spatrickdef section_ptr_refs(debugger, command, result, dict): 1242061da546Spatrick command_args = shlex.split(command) 1243061da546Spatrick usage = "usage: %prog [options] <EXPR> [EXPR ...]" 1244061da546Spatrick description = '''Searches section contents for pointer values in darwin user space programs.''' 1245061da546Spatrick parser = optparse.OptionParser( 1246061da546Spatrick description=description, 1247061da546Spatrick prog='section_ptr_refs', 1248061da546Spatrick usage=usage) 1249061da546Spatrick add_common_options(parser) 1250061da546Spatrick parser.add_option( 1251061da546Spatrick '--section', 1252061da546Spatrick action='append', 1253061da546Spatrick type='string', 1254061da546Spatrick dest='section_names', 1255061da546Spatrick help='section name to search', 1256061da546Spatrick default=list()) 1257061da546Spatrick try: 1258061da546Spatrick (options, args) = parser.parse_args(command_args) 1259061da546Spatrick except: 1260061da546Spatrick return 1261061da546Spatrick 1262061da546Spatrick options.type = 'pointer' 1263061da546Spatrick 1264061da546Spatrick sections = list() 1265061da546Spatrick section_modules = list() 1266061da546Spatrick if not options.section_names: 1267061da546Spatrick result.AppendMessage( 1268061da546Spatrick 'error: at least one section must be specified with the --section option') 1269061da546Spatrick return 1270061da546Spatrick 1271061da546Spatrick target = debugger.GetSelectedTarget() 1272061da546Spatrick for module in target.modules: 1273061da546Spatrick for section_name in options.section_names: 1274061da546Spatrick section = module.section[section_name] 1275061da546Spatrick if section: 1276061da546Spatrick sections.append(section) 1277061da546Spatrick section_modules.append(module) 1278061da546Spatrick if sections: 1279061da546Spatrick dylid_load_err = load_dylib() 1280061da546Spatrick if dylid_load_err: 1281061da546Spatrick result.AppendMessage(dylid_load_err) 1282061da546Spatrick return 1283061da546Spatrick frame = target.GetProcess().GetSelectedThread().GetSelectedFrame() 1284061da546Spatrick for expr_str in args: 1285061da546Spatrick for (idx, section) in enumerate(sections): 1286061da546Spatrick expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % ( 1287061da546Spatrick section.addr.load_addr, section.size, expr_str) 1288061da546Spatrick arg_str_description = 'section %s.%s containing "%s"' % ( 1289061da546Spatrick section_modules[idx].file.fullpath, section.name, expr_str) 1290061da546Spatrick num_matches = display_match_results( 1291061da546Spatrick target.GetProcess(), result, options, arg_str_description, expr, False) 1292061da546Spatrick if num_matches: 1293061da546Spatrick if num_matches < options.max_matches: 1294061da546Spatrick options.max_matches = options.max_matches - num_matches 1295061da546Spatrick else: 1296061da546Spatrick options.max_matches = 0 1297061da546Spatrick if options.max_matches == 0: 1298061da546Spatrick return 1299061da546Spatrick else: 1300061da546Spatrick result.AppendMessage( 1301061da546Spatrick 'error: no sections were found that match any of %s' % 1302061da546Spatrick (', '.join( 1303061da546Spatrick options.section_names))) 1304061da546Spatrick 1305061da546Spatrick 1306061da546Spatrickdef get_objc_refs_options(): 1307061da546Spatrick usage = "usage: %prog [options] <CLASS> [CLASS ...]" 1308061da546Spatrick description = '''Searches all allocations on the heap for instances of 1309061da546Spatrickobjective C classes, or any classes that inherit from the specified classes 1310061da546Spatrickin darwin user space programs. Any matches that were found will dump the malloc 1311061da546Spatrickblocks that contain the C strings and might be able to print what kind of 1312061da546Spatrickobjects the pointers are contained in using dynamic type information in the 1313061da546Spatrickprogram.''' 1314061da546Spatrick parser = optparse.OptionParser( 1315061da546Spatrick description=description, 1316061da546Spatrick prog='objc_refs', 1317061da546Spatrick usage=usage) 1318061da546Spatrick add_common_options(parser) 1319061da546Spatrick return parser 1320061da546Spatrick 1321061da546Spatrick 1322061da546Spatrickdef objc_refs(debugger, command, result, dict): 1323061da546Spatrick command_args = shlex.split(command) 1324061da546Spatrick parser = get_objc_refs_options() 1325061da546Spatrick try: 1326061da546Spatrick (options, args) = parser.parse_args(command_args) 1327061da546Spatrick except: 1328061da546Spatrick return 1329061da546Spatrick 1330061da546Spatrick process = debugger.GetSelectedTarget().GetProcess() 1331061da546Spatrick if not process: 1332061da546Spatrick result.AppendMessage('error: invalid process') 1333061da546Spatrick return 1334061da546Spatrick frame = process.GetSelectedThread().GetSelectedFrame() 1335061da546Spatrick if not frame: 1336061da546Spatrick result.AppendMessage('error: invalid frame') 1337061da546Spatrick return 1338061da546Spatrick 1339061da546Spatrick options.type = 'isa' 1340061da546Spatrick if options.format is None: 1341061da546Spatrick options.format = "A" # 'A' is "address" format 1342061da546Spatrick 1343061da546Spatrick expr_options = lldb.SBExpressionOptions() 1344061da546Spatrick expr_options.SetIgnoreBreakpoints(True) 1345061da546Spatrick expr_options.SetTimeoutInMicroSeconds( 1346061da546Spatrick 3 * 1000 * 1000) # 3 second infinite timeout 1347061da546Spatrick expr_options.SetTryAllThreads(True) 1348061da546Spatrick expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) 1349061da546Spatrick num_objc_classes_value = frame.EvaluateExpression( 1350061da546Spatrick "(int)objc_getClassList((void *)0, (int)0)", expr_options) 1351061da546Spatrick if not num_objc_classes_value.error.Success(): 1352061da546Spatrick result.AppendMessage('error: %s' % 1353061da546Spatrick num_objc_classes_value.error.GetCString()) 1354061da546Spatrick return 1355061da546Spatrick 1356061da546Spatrick num_objc_classes = num_objc_classes_value.GetValueAsUnsigned() 1357061da546Spatrick if num_objc_classes == 0: 1358061da546Spatrick result.AppendMessage('error: no objective C classes in program') 1359061da546Spatrick return 1360061da546Spatrick 1361061da546Spatrick if args: 1362061da546Spatrick # When we initialize the expression, we must define any types that 1363061da546Spatrick # we will need when looking at every allocation. We must also define 1364061da546Spatrick # a type named callback_baton_t and make an instance named "baton" 1365061da546Spatrick # and initialize it how ever we want to. The address of "baton" will 1366061da546Spatrick # be passed into our range callback. callback_baton_t must contain 1367061da546Spatrick # a member named "callback" whose type is "range_callback_t". This 1368061da546Spatrick # will be used by our zone callbacks to call the range callback for 1369061da546Spatrick # each malloc range. 1370061da546Spatrick expr_prefix = ''' 1371061da546Spatrickstruct $malloc_match { 1372061da546Spatrick void *addr; 1373061da546Spatrick uintptr_t size; 1374061da546Spatrick uintptr_t offset; 1375061da546Spatrick uintptr_t type; 1376061da546Spatrick}; 1377061da546Spatrick''' 1378061da546Spatrick 1379061da546Spatrick user_init_code_format = ''' 1380061da546Spatrick#define MAX_MATCHES %u 1381061da546Spatricktypedef int (*compare_callback_t)(const void *a, const void *b); 1382061da546Spatricktypedef struct callback_baton_t { 1383061da546Spatrick range_callback_t callback; 1384061da546Spatrick compare_callback_t compare_callback; 1385061da546Spatrick unsigned num_matches; 1386061da546Spatrick $malloc_match matches[MAX_MATCHES]; 1387061da546Spatrick void *isa; 1388061da546Spatrick Class classes[%u]; 1389061da546Spatrick} callback_baton_t; 1390061da546Spatrickcompare_callback_t compare_callback = [](const void *a, const void *b) -> int { 1391061da546Spatrick Class a_ptr = *(Class *)a; 1392061da546Spatrick Class b_ptr = *(Class *)b; 1393061da546Spatrick if (a_ptr < b_ptr) return -1; 1394061da546Spatrick if (a_ptr > b_ptr) return +1; 1395061da546Spatrick return 0; 1396061da546Spatrick}; 1397061da546Spatricktypedef Class (*class_getSuperclass_type)(void *isa); 1398061da546Spatrickrange_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 1399061da546Spatrick class_getSuperclass_type class_getSuperclass_impl = (class_getSuperclass_type)class_getSuperclass; 1400061da546Spatrick callback_baton_t *lldb_info = (callback_baton_t *)baton; 1401061da546Spatrick if (sizeof(Class) <= ptr_size) { 1402061da546Spatrick Class *curr_class_ptr = (Class *)ptr_addr; 1403061da546Spatrick Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr, 1404061da546Spatrick (const void *)lldb_info->classes, 1405061da546Spatrick sizeof(lldb_info->classes)/sizeof(Class), 1406061da546Spatrick sizeof(Class), 1407061da546Spatrick lldb_info->compare_callback); 1408061da546Spatrick if (matching_class_ptr) { 1409061da546Spatrick bool match = false; 1410061da546Spatrick if (lldb_info->isa) { 1411061da546Spatrick Class isa = *curr_class_ptr; 1412061da546Spatrick if (lldb_info->isa == isa) 1413061da546Spatrick match = true; 1414061da546Spatrick else { // if (lldb_info->objc.match_superclasses) { 1415061da546Spatrick Class super = class_getSuperclass_impl(isa); 1416061da546Spatrick while (super) { 1417061da546Spatrick if (super == lldb_info->isa) { 1418061da546Spatrick match = true; 1419061da546Spatrick break; 1420061da546Spatrick } 1421061da546Spatrick super = class_getSuperclass_impl(super); 1422061da546Spatrick } 1423061da546Spatrick } 1424061da546Spatrick } 1425061da546Spatrick else 1426061da546Spatrick match = true; 1427061da546Spatrick if (match) { 1428061da546Spatrick if (lldb_info->num_matches < MAX_MATCHES) { 1429061da546Spatrick lldb_info->matches[lldb_info->num_matches].addr = (void*)ptr_addr; 1430061da546Spatrick lldb_info->matches[lldb_info->num_matches].size = ptr_size; 1431061da546Spatrick lldb_info->matches[lldb_info->num_matches].offset = 0; 1432061da546Spatrick lldb_info->matches[lldb_info->num_matches].type = type; 1433061da546Spatrick ++lldb_info->num_matches; 1434061da546Spatrick } 1435061da546Spatrick } 1436061da546Spatrick } 1437061da546Spatrick } 1438061da546Spatrick}; 1439061da546Spatrickcallback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} }; 1440061da546Spatrickint nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class)); 1441061da546Spatrick(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);''' 1442061da546Spatrick # We must also define a snippet of code to be run that returns 1443061da546Spatrick # the result of the expression we run. 1444061da546Spatrick # Here we return NULL if our pointer was not found in any malloc blocks, 1445061da546Spatrick # and we return the address of the matches array so we can then access 1446061da546Spatrick # the matching results 1447061da546Spatrick user_return_code = '''if (baton.num_matches < MAX_MATCHES) 1448061da546Spatrick baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 1449061da546Spatrick baton.matches''' 1450061da546Spatrick # Iterate through all of our ObjC class name arguments 1451061da546Spatrick for class_name in args: 1452061da546Spatrick addr_expr_str = "(void *)[%s class]" % class_name 1453061da546Spatrick expr_options = lldb.SBExpressionOptions() 1454061da546Spatrick expr_options.SetIgnoreBreakpoints(True) 1455061da546Spatrick expr_options.SetTimeoutInMicroSeconds( 1456061da546Spatrick 1 * 1000 * 1000) # 1 second timeout 1457061da546Spatrick expr_options.SetTryAllThreads(True) 1458061da546Spatrick expr_options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) 1459061da546Spatrick expr_sbvalue = frame.EvaluateExpression( 1460061da546Spatrick addr_expr_str, expr_options) 1461061da546Spatrick if expr_sbvalue.error.Success(): 1462061da546Spatrick isa = expr_sbvalue.unsigned 1463061da546Spatrick if isa: 1464061da546Spatrick options.type = 'isa' 1465061da546Spatrick result.AppendMessage( 1466061da546Spatrick 'Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % 1467061da546Spatrick (class_name, isa)) 1468061da546Spatrick user_init_code = user_init_code_format % ( 1469061da546Spatrick options.max_matches, num_objc_classes, isa) 1470061da546Spatrick expr = get_iterate_memory_expr( 1471061da546Spatrick options, process, user_init_code, user_return_code) 1472061da546Spatrick arg_str_description = 'objective C classes with isa 0x%x' % isa 1473061da546Spatrick display_match_results( 1474061da546Spatrick process, 1475061da546Spatrick result, 1476061da546Spatrick options, 1477061da546Spatrick arg_str_description, 1478061da546Spatrick expr, 1479061da546Spatrick True, 1480061da546Spatrick expr_prefix) 1481061da546Spatrick else: 1482061da546Spatrick result.AppendMessage( 1483061da546Spatrick 'error: Can\'t find isa for an ObjC class named "%s"' % 1484061da546Spatrick (class_name)) 1485061da546Spatrick else: 1486061da546Spatrick result.AppendMessage( 1487061da546Spatrick 'error: expression error for "%s": %s' % 1488061da546Spatrick (addr_expr_str, expr_sbvalue.error)) 1489061da546Spatrick else: 1490061da546Spatrick result.AppendMessage( 1491061da546Spatrick 'error: command takes one or more C string arguments') 1492061da546Spatrick 1493061da546Spatrickif __name__ == '__main__': 1494061da546Spatrick lldb.debugger = lldb.SBDebugger.Create() 1495061da546Spatrick 1496*f6aab3d8Srobertdef __lldb_init_module(debugger, internal_dict): 1497061da546Spatrick # Make the options so we can generate the help text for the new LLDB 1498061da546Spatrick # command line command prior to registering it with LLDB below. This way 1499061da546Spatrick # if clients in LLDB type "help malloc_info", they will see the exact same 1500061da546Spatrick # output as typing "malloc_info --help". 1501061da546Spatrick ptr_refs.__doc__ = get_ptr_refs_options().format_help() 1502061da546Spatrick cstr_refs.__doc__ = get_cstr_refs_options().format_help() 1503061da546Spatrick malloc_info.__doc__ = get_malloc_info_options().format_help() 1504061da546Spatrick objc_refs.__doc__ = get_objc_refs_options().format_help() 1505*f6aab3d8Srobert debugger.HandleCommand( 1506*f6aab3d8Srobert 'command script add -o -f %s.ptr_refs ptr_refs' % 1507061da546Spatrick __name__) 1508*f6aab3d8Srobert debugger.HandleCommand( 1509*f6aab3d8Srobert 'command script add -o -f %s.cstr_refs cstr_refs' % 1510061da546Spatrick __name__) 1511*f6aab3d8Srobert debugger.HandleCommand( 1512*f6aab3d8Srobert 'command script add -o -f %s.malloc_info malloc_info' % 1513061da546Spatrick __name__) 1514*f6aab3d8Srobert debugger.HandleCommand( 1515*f6aab3d8Srobert 'command script add -o -f %s.find_variable find_variable' % 1516061da546Spatrick __name__) 1517*f6aab3d8Srobert # debugger.HandleCommand('command script add -o -f %s.section_ptr_refs section_ptr_refs' % package_name) 1518*f6aab3d8Srobert debugger.HandleCommand( 1519*f6aab3d8Srobert 'command script add -o -f %s.objc_refs objc_refs' % 1520061da546Spatrick __name__) 1521061da546Spatrick 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.') 1522