1#!/usr/bin/python 2 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: 7# 8# (lldb) script import lldb.macosx.heap 9#---------------------------------------------------------------------- 10 11import lldb 12import commands 13import optparse 14import os 15import os.path 16import re 17import shlex 18import string 19import tempfile 20import lldb.utils.symbolication 21 22g_libheap_dylib_dir = None 23g_libheap_dylib_dict = dict() 24 25def get_iterate_memory_expr(options, process, user_init_code, user_return_code): 26 expr = ''' 27typedef unsigned natural_t; 28typedef uintptr_t vm_size_t; 29typedef uintptr_t vm_address_t; 30typedef natural_t task_t; 31typedef int kern_return_t; 32#define KERN_SUCCESS 0 33typedef void (*range_callback_t)(task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size); 34'''; 35 if options.search_vm_regions: 36 expr += ''' 37typedef int vm_prot_t; 38typedef unsigned int vm_inherit_t; 39typedef unsigned long long memory_object_offset_t; 40typedef unsigned int boolean_t; 41typedef int vm_behavior_t; 42typedef uint32_t vm32_object_id_t; 43typedef natural_t mach_msg_type_number_t; 44typedef uint64_t mach_vm_address_t; 45typedef uint64_t mach_vm_offset_t; 46typedef uint64_t mach_vm_size_t; 47typedef uint64_t vm_map_offset_t; 48typedef uint64_t vm_map_address_t; 49typedef uint64_t vm_map_size_t; 50#define VM_PROT_NONE ((vm_prot_t) 0x00) 51#define VM_PROT_READ ((vm_prot_t) 0x01) 52#define VM_PROT_WRITE ((vm_prot_t) 0x02) 53#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) 54typedef struct vm_region_submap_short_info_data_64_t { 55 vm_prot_t protection; 56 vm_prot_t max_protection; 57 vm_inherit_t inheritance; 58 memory_object_offset_t offset; // offset into object/map 59 unsigned int user_tag; // user tag on map entry 60 unsigned int ref_count; // obj/map mappers, etc 61 unsigned short shadow_depth; // only for obj 62 unsigned char external_pager; // only for obj 63 unsigned char share_mode; // see enumeration 64 boolean_t is_submap; // submap vs obj 65 vm_behavior_t behavior; // access behavior hint 66 vm32_object_id_t object_id; // obj/map name, not a handle 67 unsigned short user_wired_count; 68} vm_region_submap_short_info_data_64_t; 69#define VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 ((mach_msg_type_number_t)(sizeof(vm_region_submap_short_info_data_64_t)/sizeof(int)))'''; 70 if user_init_code: 71 expr += user_init_code; 72 expr += ''' 73task_t task = (task_t)mach_task_self(); 74mach_vm_address_t vm_region_base_addr; 75mach_vm_size_t vm_region_size; 76natural_t vm_region_depth; 77vm_region_submap_short_info_data_64_t vm_region_info; 78kern_return_t err; 79for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) 80{ 81 mach_msg_type_number_t vm_region_info_size = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; 82 err = (kern_return_t)mach_vm_region_recurse (task, 83 &vm_region_base_addr, 84 &vm_region_size, 85 &vm_region_depth, 86 &vm_region_info, 87 &vm_region_info_size); 88 if (err) 89 break; 90 // Check all read + write regions. This will cover the thread stacks 91 // and any regions of memory like __DATA segments, that might contain 92 // data we are looking for 93 if (vm_region_info.protection & VM_PROT_WRITE && 94 vm_region_info.protection & VM_PROT_READ) 95 { 96 baton.callback (task, 97 &baton, 98 64, 99 vm_region_base_addr, 100 vm_region_size); 101 } 102}''' 103 else: 104 if options.search_stack: 105 expr += get_thread_stack_ranges_struct (process) 106 if options.search_segments: 107 expr += get_sections_ranges_struct (process) 108 if user_init_code: 109 expr += user_init_code 110 if options.search_heap: 111 expr += ''' 112#define MALLOC_PTR_IN_USE_RANGE_TYPE 1 113typedef struct vm_range_t { 114 vm_address_t address; 115 vm_size_t size; 116} vm_range_t; 117typedef kern_return_t (*memory_reader_t)(task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory); 118typedef void (*vm_range_recorder_t)(task_t task, void *baton, unsigned type, vm_range_t *range, unsigned size); 119typedef struct malloc_introspection_t { 120 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 */ 121} malloc_introspection_t; 122typedef struct malloc_zone_t { 123 void *reserved1[12]; 124 struct malloc_introspection_t *introspect; 125} malloc_zone_t; 126memory_reader_t task_peek = [](task_t task, vm_address_t remote_address, vm_size_t size, void **local_memory) -> kern_return_t { 127 *local_memory = (void*) remote_address; 128 return KERN_SUCCESS; 129}; 130vm_address_t *zones = 0; 131unsigned int num_zones = 0;task_t task = 0; 132kern_return_t err = (kern_return_t)malloc_get_all_zones (task, task_peek, &zones, &num_zones); 133if (KERN_SUCCESS == err) 134{ 135 for (unsigned int i=0; i<num_zones; ++i) 136 { 137 const malloc_zone_t *zone = (const malloc_zone_t *)zones[i]; 138 if (zone && zone->introspect) 139 zone->introspect->enumerator (task, 140 &baton, 141 MALLOC_PTR_IN_USE_RANGE_TYPE, 142 (vm_address_t)zone, 143 task_peek, 144 [] (task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned size) -> void 145 { 146 range_callback_t callback = ((callback_baton_t *)baton)->callback; 147 for (unsigned i=0; i<size; ++i) 148 { 149 callback (task, baton, type, ranges[i].address, ranges[i].size); 150 } 151 }); 152 } 153}''' 154 155 if options.search_stack: 156 expr += ''' 157// Call the callback for the thread stack ranges 158for (uint32_t i=0; i<NUM_STACKS; ++i) { 159 range_callback(task, &baton, 8, stacks[i].base, stacks[i].size); 160 if (STACK_RED_ZONE_SIZE > 0) { 161 range_callback(task, &baton, 16, stacks[i].base - STACK_RED_ZONE_SIZE, STACK_RED_ZONE_SIZE); 162 } 163} 164 ''' 165 166 if options.search_segments: 167 expr += ''' 168// Call the callback for all segments 169for (uint32_t i=0; i<NUM_SEGMENTS; ++i) 170 range_callback(task, &baton, 32, segments[i].base, segments[i].size);''' 171 172 if user_return_code: 173 expr += "\n%s" % (user_return_code,) 174 175 return expr 176 177def get_member_types_for_offset(value_type, offset, member_list): 178 member = value_type.GetFieldAtIndex(0) 179 search_bases = False 180 if member: 181 if member.GetOffsetInBytes() <= offset: 182 for field_idx in range (value_type.GetNumberOfFields()): 183 member = value_type.GetFieldAtIndex(field_idx) 184 member_byte_offset = member.GetOffsetInBytes() 185 member_end_byte_offset = member_byte_offset + member.type.size 186 if member_byte_offset <= offset and offset < member_end_byte_offset: 187 member_list.append(member) 188 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list) 189 return 190 else: 191 search_bases = True 192 else: 193 search_bases = True 194 if search_bases: 195 for field_idx in range (value_type.GetNumberOfDirectBaseClasses()): 196 member = value_type.GetDirectBaseClassAtIndex(field_idx) 197 member_byte_offset = member.GetOffsetInBytes() 198 member_end_byte_offset = member_byte_offset + member.type.size 199 if member_byte_offset <= offset and offset < member_end_byte_offset: 200 member_list.append(member) 201 get_member_types_for_offset (member.type, offset - member_byte_offset, member_list) 202 return 203 for field_idx in range (value_type.GetNumberOfVirtualBaseClasses()): 204 member = value_type.GetVirtualBaseClassAtIndex(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 (member.type, offset - member_byte_offset, member_list) 210 return 211 212def append_regex_callback(option, opt, value, parser): 213 try: 214 ivar_regex = re.compile(value) 215 parser.values.ivar_regex_blacklist.append(ivar_regex) 216 except: 217 print 'error: an exception was thrown when compiling the ivar regular expression for "%s"' % value 218 219def add_common_options(parser): 220 parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) 221 parser.add_option('-t', '--type', action='store_true', dest='print_type', help='print the full value of the type for each matching malloc block', default=False) 222 parser.add_option('-o', '--po', action='store_true', dest='print_object_description', help='print the object descriptions for any matches', default=False) 223 parser.add_option('-z', '--size', action='store_true', dest='show_size', help='print the allocation size in bytes', default=False) 224 parser.add_option('-r', '--range', action='store_true', dest='show_range', help='print the allocation address range instead of just the allocation base address', default=False) 225 parser.add_option('-m', '--memory', action='store_true', dest='memory', help='dump the memory for each matching block', default=False) 226 parser.add_option('-f', '--format', type='string', dest='format', help='the format to use when dumping memory if --memory is specified', default=None) 227 parser.add_option('-I', '--omit-ivar-regex', type='string', action='callback', callback=append_regex_callback, dest='ivar_regex_blacklist', default=[], help='specify one or more regular expressions used to backlist any matches that are in ivars') 228 parser.add_option('-s', '--stack', action='store_true', dest='stack', help='gets the stack that allocated each malloc block if MallocStackLogging is enabled', default=False) 229 parser.add_option('-S', '--stack-history', action='store_true', dest='stack_history', help='gets the stack history for all allocations whose start address matches each malloc block if MallocStackLogging is enabled', default=False) 230 parser.add_option('-F', '--max-frames', type='int', dest='max_frames', help='the maximum number of stack frames to print when using the --stack or --stack-history options (default=128)', default=128) 231 parser.add_option('-H', '--max-history', type='int', dest='max_history', help='the maximum number of stack history backtraces to print for each allocation when using the --stack-history option (default=16)', default=16) 232 parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=32) 233 parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1) 234 parser.add_option('--ignore-stack', action='store_false', dest='search_stack', help="Don't search the stack when enumerating memory", default=True) 235 parser.add_option('--ignore-heap', action='store_false', dest='search_heap', help="Don't search the heap allocations when enumerating memory", default=True) 236 parser.add_option('--ignore-segments', action='store_false', dest='search_segments', help="Don't search readable executable segments enumerating memory", default=True) 237 parser.add_option('-V', '--vm-regions', action='store_true', dest='search_vm_regions', help='Check all VM regions instead of searching the heap, stack and segments', default=False) 238 239def type_flags_to_string(type_flags): 240 if type_flags == 0: 241 type_str = 'free' 242 elif type_flags & 2: 243 type_str = 'malloc' 244 elif type_flags & 4: 245 type_str = 'free' 246 elif type_flags & 1: 247 type_str = 'generic' 248 elif type_flags & 8: 249 type_str = 'stack' 250 elif type_flags & 16: 251 type_str = 'stack (red zone)' 252 elif type_flags & 32: 253 type_str = 'segment' 254 elif type_flags & 64: 255 type_str = 'vm_region' 256 else: 257 type_str = hex(type_flags) 258 return type_str 259 260def type_flags_to_description(type_flags, ptr_addr, ptr_size, offset): 261 show_offset = False 262 if type_flags == 0 or type_flags & 4: 263 type_str = 'free(%#x)' % (ptr_addr,) 264 elif type_flags & 2 or type_flags & 1: 265 type_str = 'malloc(%6u) -> %#x' % (ptr_size, ptr_addr) 266 show_offset = True 267 elif type_flags & 8: 268 type_str = 'stack' 269 elif type_flags & 16: 270 type_str = 'stack (red zone)' 271 elif type_flags & 32: 272 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset) 273 type_str = 'segment [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) 274 elif type_flags & 64: 275 sb_addr = lldb.debugger.GetSelectedTarget().ResolveLoadAddress(ptr_addr + offset) 276 type_str = 'vm_region [%#x - %#x), %s + %u, %s' % (ptr_addr, ptr_addr + ptr_size, sb_addr.section.name, sb_addr.offset, sb_addr) 277 else: 278 type_str = '%#x' % (ptr_addr,) 279 show_offset = True 280 if show_offset and offset != 0: 281 type_str += ' + %-6u' % (offset,) 282 return type_str 283 284def dump_stack_history_entry(options, result, stack_history_entry, idx): 285 address = int(stack_history_entry.address) 286 if address: 287 type_flags = int(stack_history_entry.type_flags) 288 symbolicator = lldb.utils.symbolication.Symbolicator() 289 symbolicator.target = lldb.debugger.GetSelectedTarget() 290 type_str = type_flags_to_string(type_flags) 291 result.AppendMessage('stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str)) 292 frame_idx = 0 293 idx = 0 294 pc = int(stack_history_entry.frames[idx]) 295 while pc != 0: 296 if pc >= 0x1000: 297 frames = symbolicator.symbolicate(pc) 298 if frames: 299 for frame in frames: 300 result.AppendMessage(' [%u] %s' % (frame_idx, frame)) 301 frame_idx += 1 302 else: 303 result.AppendMessage(' [%u] 0x%x' % (frame_idx, pc)) 304 frame_idx += 1 305 idx = idx + 1 306 pc = int(stack_history_entry.frames[idx]) 307 else: 308 pc = 0 309 if idx >= options.max_frames: 310 result.AppendMessage('warning: the max number of stack frames (%u) was reached, use the "--max-frames=<COUNT>" option to see more frames' % (options.max_frames)) 311 312 result.AppendMessage('') 313 314def dump_stack_history_entries(options, result, addr, history): 315 # malloc_stack_entry *get_stack_history_for_address (const void * addr) 316 single_expr = ''' 317typedef int kern_return_t; 318#define MAX_FRAMES %u 319typedef struct $malloc_stack_entry { 320 uint64_t address; 321 uint64_t argument; 322 uint32_t type_flags; 323 uint32_t num_frames; 324 uint64_t frames[512]; 325 kern_return_t err; 326} $malloc_stack_entry; 327typedef unsigned task_t; 328$malloc_stack_entry stack; 329stack.address = 0x%x; 330stack.type_flags = 2; 331stack.num_frames = 0; 332stack.frames[0] = 0; 333uint32_t max_stack_frames = MAX_FRAMES; 334stack.err = (kern_return_t)__mach_stack_logging_get_frames ( 335 (task_t)mach_task_self(), 336 stack.address, 337 &stack.frames[0], 338 max_stack_frames, 339 &stack.num_frames); 340if (stack.num_frames < MAX_FRAMES) 341 stack.frames[stack.num_frames] = 0; 342else 343 stack.frames[MAX_FRAMES-1] = 0; 344stack''' % (options.max_frames, addr); 345 346 history_expr = ''' 347typedef int kern_return_t; 348typedef unsigned task_t; 349#define MAX_FRAMES %u 350#define MAX_HISTORY %u 351typedef struct mach_stack_logging_record_t { 352 uint32_t type_flags; 353 uint64_t stack_identifier; 354 uint64_t argument; 355 uint64_t address; 356} mach_stack_logging_record_t; 357typedef void (*enumerate_callback_t)(mach_stack_logging_record_t, void *); 358typedef struct malloc_stack_entry { 359 uint64_t address; 360 uint64_t argument; 361 uint32_t type_flags; 362 uint32_t num_frames; 363 uint64_t frames[MAX_FRAMES]; 364 kern_return_t frames_err; 365} malloc_stack_entry; 366typedef struct $malloc_stack_history { 367 task_t task; 368 unsigned idx; 369 malloc_stack_entry entries[MAX_HISTORY]; 370} $malloc_stack_history; 371$malloc_stack_history info = { (task_t)mach_task_self(), 0 }; 372uint32_t max_stack_frames = MAX_FRAMES; 373enumerate_callback_t callback = [] (mach_stack_logging_record_t stack_record, void *baton) -> void { 374 $malloc_stack_history *info = ($malloc_stack_history *)baton; 375 if (info->idx < MAX_HISTORY) { 376 malloc_stack_entry *stack_entry = &(info->entries[info->idx]); 377 stack_entry->address = stack_record.address; 378 stack_entry->type_flags = stack_record.type_flags; 379 stack_entry->argument = stack_record.argument; 380 stack_entry->num_frames = 0; 381 stack_entry->frames[0] = 0; 382 stack_entry->frames_err = (kern_return_t)__mach_stack_logging_frames_for_uniqued_stack ( 383 info->task, 384 stack_record.stack_identifier, 385 stack_entry->frames, 386 (uint32_t)MAX_FRAMES, 387 &stack_entry->num_frames); 388 // Terminate the frames with zero if there is room 389 if (stack_entry->num_frames < MAX_FRAMES) 390 stack_entry->frames[stack_entry->num_frames] = 0; 391 } 392 ++info->idx; 393}; 394(kern_return_t)__mach_stack_logging_enumerate_records (info.task, (uint64_t)0x%x, callback, &info); 395info''' % (options.max_frames, options.max_history, addr); 396 397 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() 398 if history: 399 expr = history_expr 400 else: 401 expr = single_expr 402 expr_options = lldb.SBExpressionOptions() 403 expr_options.SetIgnoreBreakpoints(True); 404 expr_options.SetTimeoutInMicroSeconds (5*1000*1000) # 5 second timeout 405 expr_options.SetTryAllThreads (True) 406 expr_sbvalue = frame.EvaluateExpression (expr, expr_options) 407 if options.verbose: 408 print "expression:" 409 print expr 410 print "expression result:" 411 print expr_sbvalue 412 if expr_sbvalue.error.Success(): 413 if history: 414 malloc_stack_history = lldb.value(expr_sbvalue) 415 num_stacks = int(malloc_stack_history.idx) 416 if num_stacks <= options.max_history: 417 i_max = num_stacks 418 else: 419 i_max = options.max_history 420 for i in range(i_max): 421 stack_history_entry = malloc_stack_history.entries[i] 422 dump_stack_history_entry(options, result, stack_history_entry, i) 423 if num_stacks > options.max_history: 424 result.AppendMessage('warning: the max number of stacks (%u) was reached, use the "--max-history=%u" option to see all of the stacks' % (options.max_history, num_stacks)) 425 else: 426 stack_history_entry = lldb.value(expr_sbvalue) 427 dump_stack_history_entry(options, result, stack_history_entry, 0) 428 429 else: 430 result.AppendMessage('error: expression failed "%s" => %s' % (expr, expr_sbvalue.error)) 431 432 433def display_match_results (result, options, arg_str_description, expr, print_no_matches = True): 434 frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() 435 if not frame: 436 result.AppendMessage('error: invalid frame') 437 return 0 438 expr_options = lldb.SBExpressionOptions() 439 expr_options.SetIgnoreBreakpoints(True); 440 expr_options.SetFetchDynamicValue(lldb.eNoDynamicValues); 441 expr_options.SetTimeoutInMicroSeconds (30*1000*1000) # 30 second timeout 442 expr_options.SetTryAllThreads (False) 443 expr_sbvalue = frame.EvaluateExpression (expr, expr_options) 444 if options.verbose: 445 print "expression:" 446 print expr 447 print "expression result:" 448 print expr_sbvalue 449 if expr_sbvalue.error.Success(): 450 if expr_sbvalue.unsigned: 451 match_value = lldb.value(expr_sbvalue) 452 i = 0 453 match_idx = 0 454 while 1: 455 print_entry = True 456 match_entry = match_value[i]; i += 1 457 if i > options.max_matches: 458 result.AppendMessage('warning: the max number of matches (%u) was reached, use the --max-matches option to get more results' % (options.max_matches)) 459 break 460 malloc_addr = match_entry.addr.sbvalue.unsigned 461 if malloc_addr == 0: 462 break 463 malloc_size = int(match_entry.size) 464 offset = int(match_entry.offset) 465 466 if options.offset >= 0 and options.offset != offset: 467 print_entry = False 468 else: 469 match_addr = malloc_addr + offset 470 type_flags = int(match_entry.type) 471 #result.AppendMessage (hex(malloc_addr + offset)) 472 if type_flags == 64: 473 search_stack_old = options.search_stack 474 search_segments_old = options.search_segments 475 search_heap_old = options.search_heap 476 search_vm_regions = options.search_vm_regions 477 options.search_stack = True 478 options.search_segments = True 479 options.search_heap = True 480 options.search_vm_regions = False 481 if malloc_info_impl (lldb.debugger, result, options, [hex(malloc_addr + offset)]): 482 print_entry = False 483 options.search_stack = search_stack_old 484 options.search_segments = search_segments_old 485 options.search_heap = search_heap_old 486 options.search_vm_regions = search_vm_regions 487 if print_entry: 488 description = '%#16.16x: %s' % (match_addr, type_flags_to_description(type_flags, malloc_addr, malloc_size, offset)) 489 if options.show_size: 490 description += ' <%5u>' % (malloc_size) 491 if options.show_range: 492 description += ' [%#x - %#x)' % (malloc_addr, malloc_addr + malloc_size) 493 derefed_dynamic_value = None 494 dynamic_value = match_entry.addr.sbvalue.GetDynamicValue(lldb.eDynamicCanRunTarget) 495 if dynamic_value.type.name == 'void *': 496 if options.type == 'pointer' and malloc_size == 4096: 497 error = lldb.SBError() 498 process = expr_sbvalue.GetProcess() 499 target = expr_sbvalue.GetTarget() 500 data = bytearray(process.ReadMemory(malloc_addr, 16, error)) 501 if data == '\xa1\xa1\xa1\xa1AUTORELEASE!': 502 ptr_size = target.addr_size 503 thread = process.ReadUnsignedFromMemory (malloc_addr + 16 + ptr_size, ptr_size, error) 504 # 4 bytes 0xa1a1a1a1 505 # 12 bytes 'AUTORELEASE!' 506 # ptr bytes autorelease insertion point 507 # ptr bytes pthread_t 508 # ptr bytes next colder page 509 # ptr bytes next hotter page 510 # 4 bytes this page's depth in the list 511 # 4 bytes high-water mark 512 description += ' AUTORELEASE! for pthread_t %#x' % (thread) 513 # else: 514 # description += 'malloc(%u)' % (malloc_size) 515 # else: 516 # description += 'malloc(%u)' % (malloc_size) 517 else: 518 derefed_dynamic_value = dynamic_value.deref 519 if derefed_dynamic_value: 520 derefed_dynamic_type = derefed_dynamic_value.type 521 derefed_dynamic_type_size = derefed_dynamic_type.size 522 derefed_dynamic_type_name = derefed_dynamic_type.name 523 description += ' ' 524 description += derefed_dynamic_type_name 525 if offset < derefed_dynamic_type_size: 526 member_list = list(); 527 get_member_types_for_offset (derefed_dynamic_type, offset, member_list) 528 if member_list: 529 member_path = '' 530 for member in member_list: 531 member_name = member.name 532 if member_name: 533 if member_path: 534 member_path += '.' 535 member_path += member_name 536 if member_path: 537 if options.ivar_regex_blacklist: 538 for ivar_regex in options.ivar_regex_blacklist: 539 if ivar_regex.match(member_path): 540 print_entry = False 541 description += '.%s' % (member_path) 542 else: 543 description += '%u bytes after %s' % (offset - derefed_dynamic_type_size, derefed_dynamic_type_name) 544 else: 545 # strip the "*" from the end of the name since we were unable to dereference this 546 description += dynamic_value.type.name[0:-1] 547 if print_entry: 548 match_idx += 1 549 result_output = '' 550 if description: 551 result_output += description 552 if options.print_type and derefed_dynamic_value: 553 result_output += ' %s' % (derefed_dynamic_value) 554 if options.print_object_description and dynamic_value: 555 desc = dynamic_value.GetObjectDescription() 556 if desc: 557 result_output += '\n%s' % (desc) 558 if result_output: 559 result.AppendMessage(result_output) 560 if options.memory: 561 cmd_result = lldb.SBCommandReturnObject() 562 if options.format == None: 563 memory_command = "memory read --force 0x%x 0x%x" % (malloc_addr, malloc_addr + malloc_size) 564 else: 565 memory_command = "memory read --force -f %s 0x%x 0x%x" % (options.format, malloc_addr, malloc_addr + malloc_size) 566 if options.verbose: 567 result.AppendMessage(memory_command) 568 lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result) 569 result.AppendMessage(cmd_result.GetOutput()) 570 if options.stack_history: 571 dump_stack_history_entries(options, result, malloc_addr, 1) 572 elif options.stack: 573 dump_stack_history_entries(options, result, malloc_addr, 0) 574 return i 575 elif print_no_matches: 576 result.AppendMessage('no matches found for %s' % (arg_str_description)) 577 else: 578 result.AppendMessage(str(expr_sbvalue.error)) 579 return 0 580 581def get_ptr_refs_options (): 582 usage = "usage: %prog [options] <EXPR> [EXPR ...]" 583 description='''Searches all allocations on the heap for pointer values on 584darwin user space programs. Any matches that were found will dump the malloc 585blocks that contain the pointers and might be able to print what kind of 586objects the pointers are contained in using dynamic type information in the 587program.''' 588 parser = optparse.OptionParser(description=description, prog='ptr_refs',usage=usage) 589 add_common_options(parser) 590 return parser 591 592def ptr_refs(debugger, command, result, dict): 593 command_args = shlex.split(command) 594 parser = get_ptr_refs_options() 595 try: 596 (options, args) = parser.parse_args(command_args) 597 except: 598 return 599 600 process = lldb.debugger.GetSelectedTarget().GetProcess() 601 if not process: 602 result.AppendMessage('error: invalid process') 603 return 604 frame = process.GetSelectedThread().GetSelectedFrame() 605 if not frame: 606 result.AppendMessage('error: invalid frame') 607 return 608 609 options.type = 'pointer' 610 if options.format == None: 611 options.format = "A" # 'A' is "address" format 612 613 if args: 614 # When we initialize the expression, we must define any types that 615 # we will need when looking at every allocation. We must also define 616 # a type named callback_baton_t and make an instance named "baton" 617 # and initialize it how ever we want to. The address of "baton" will 618 # be passed into our range callback. callback_baton_t must contain 619 # a member named "callback" whose type is "range_callback_t". This 620 # will be used by our zone callbacks to call the range callback for 621 # each malloc range. 622 user_init_code_format = ''' 623#define MAX_MATCHES %u 624struct $malloc_match { 625 void *addr; 626 uintptr_t size; 627 uintptr_t offset; 628 uintptr_t type; 629}; 630typedef struct callback_baton_t { 631 range_callback_t callback; 632 unsigned num_matches; 633 $malloc_match matches[MAX_MATCHES]; 634 void *ptr; 635} callback_baton_t; 636range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 637 callback_baton_t *info = (callback_baton_t *)baton; 638 typedef void* T; 639 const unsigned size = sizeof(T); 640 T *array = (T*)ptr_addr; 641 for (unsigned idx = 0; ((idx + 1) * sizeof(T)) <= ptr_size; ++idx) { 642 if (array[idx] == info->ptr) { 643 if (info->num_matches < MAX_MATCHES) { 644 info->matches[info->num_matches].addr = (void*)ptr_addr; 645 info->matches[info->num_matches].size = ptr_size; 646 info->matches[info->num_matches].offset = idx*sizeof(T); 647 info->matches[info->num_matches].type = type; 648 ++info->num_matches; 649 } 650 } 651 } 652}; 653callback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; 654''' 655 # We must also define a snippet of code to be run that returns 656 # the result of the expression we run. 657 # Here we return NULL if our pointer was not found in any malloc blocks, 658 # and we return the address of the matches array so we can then access 659 # the matching results 660 user_return_code = '''if (baton.num_matches < MAX_MATCHES) 661 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 662baton.matches''' 663 # Iterate through all of our pointer expressions and display the results 664 for ptr_expr in args: 665 user_init_code = user_init_code_format % (options.max_matches, ptr_expr) 666 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) 667 arg_str_description = 'malloc block containing pointer %s' % ptr_expr 668 display_match_results (result, options, arg_str_description, expr) 669 else: 670 result.AppendMessage('error: no pointer arguments were given') 671 672def get_cstr_refs_options(): 673 usage = "usage: %prog [options] <CSTR> [CSTR ...]" 674 description='''Searches all allocations on the heap for C string values on 675darwin user space programs. Any matches that were found will dump the malloc 676blocks that contain the C strings and might be able to print what kind of 677objects the pointers are contained in using dynamic type information in the 678program.''' 679 parser = optparse.OptionParser(description=description, prog='cstr_refs',usage=usage) 680 add_common_options(parser) 681 return parser 682 683def cstr_refs(debugger, command, result, dict): 684 command_args = shlex.split(command) 685 parser = get_cstr_refs_options(); 686 try: 687 (options, args) = parser.parse_args(command_args) 688 except: 689 return 690 691 process = lldb.debugger.GetSelectedTarget().GetProcess() 692 if not process: 693 result.AppendMessage('error: invalid process') 694 return 695 frame = process.GetSelectedThread().GetSelectedFrame() 696 if not frame: 697 result.AppendMessage('error: invalid frame') 698 return 699 700 701 options.type = 'cstr' 702 if options.format == None: 703 options.format = "Y" # 'Y' is "bytes with ASCII" format 704 705 if args: 706 # When we initialize the expression, we must define any types that 707 # we will need when looking at every allocation. We must also define 708 # a type named callback_baton_t and make an instance named "baton" 709 # and initialize it how ever we want to. The address of "baton" will 710 # be passed into our range callback. callback_baton_t must contain 711 # a member named "callback" whose type is "range_callback_t". This 712 # will be used by our zone callbacks to call the range callback for 713 # each malloc range. 714 user_init_code_format = ''' 715#define MAX_MATCHES %u 716struct $malloc_match { 717 void *addr; 718 uintptr_t size; 719 uintptr_t offset; 720 uintptr_t type; 721}; 722typedef struct callback_baton_t { 723 range_callback_t callback; 724 unsigned num_matches; 725 $malloc_match matches[MAX_MATCHES]; 726 const char *cstr; 727 unsigned cstr_len; 728} callback_baton_t; 729range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 730 callback_baton_t *info = (callback_baton_t *)baton; 731 if (info->cstr_len < ptr_size) { 732 const char *begin = (const char *)ptr_addr; 733 const char *end = begin + ptr_size - info->cstr_len; 734 for (const char *s = begin; s < end; ++s) { 735 if ((int)memcmp(s, info->cstr, info->cstr_len) == 0) { 736 if (info->num_matches < MAX_MATCHES) { 737 info->matches[info->num_matches].addr = (void*)ptr_addr; 738 info->matches[info->num_matches].size = ptr_size; 739 info->matches[info->num_matches].offset = s - begin; 740 info->matches[info->num_matches].type = type; 741 ++info->num_matches; 742 } 743 } 744 } 745 } 746}; 747const char *cstr = "%s"; 748callback_baton_t baton = { range_callback, 0, {0}, cstr, (unsigned)strlen(cstr) };''' 749 # We must also define a snippet of code to be run that returns 750 # the result of the expression we run. 751 # Here we return NULL if our pointer was not found in any malloc blocks, 752 # and we return the address of the matches array so we can then access 753 # the matching results 754 user_return_code = '''if (baton.num_matches < MAX_MATCHES) 755 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 756baton.matches''' 757 # Iterate through all of our pointer expressions and display the results 758 for cstr in args: 759 user_init_code = user_init_code_format % (options.max_matches, cstr) 760 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) 761 arg_str_description = 'malloc block containing "%s"' % cstr 762 display_match_results (result, options, arg_str_description, expr) 763 else: 764 result.AppendMessage('error: command takes one or more C string arguments') 765 766 767def get_malloc_info_options(): 768 usage = "usage: %prog [options] <EXPR> [EXPR ...]" 769 description='''Searches the heap a malloc block that contains the addresses 770specified as one or more address expressions. Any matches that were found will 771dump the malloc blocks that match or contain the specified address. The matching 772blocks might be able to show what kind of objects they are using dynamic type 773information in the program.''' 774 parser = optparse.OptionParser(description=description, prog='malloc_info',usage=usage) 775 add_common_options(parser) 776 return parser 777 778def malloc_info(debugger, command, result, dict): 779 command_args = shlex.split(command) 780 parser = get_malloc_info_options() 781 try: 782 (options, args) = parser.parse_args(command_args) 783 except: 784 return 785 malloc_info_impl (debugger, result, options, args) 786 787def malloc_info_impl (debugger, result, options, args): 788 # We are specifically looking for something on the heap only 789 options.type = 'malloc_info' 790 791 process = lldb.debugger.GetSelectedTarget().GetProcess() 792 if not process: 793 result.AppendMessage('error: invalid process') 794 return 795 frame = process.GetSelectedThread().GetSelectedFrame() 796 if not frame: 797 result.AppendMessage('error: invalid frame') 798 return 799 800 user_init_code_format = ''' 801struct $malloc_match { 802 void *addr; 803 uintptr_t size; 804 uintptr_t offset; 805 uintptr_t type; 806}; 807typedef struct callback_baton_t { 808 range_callback_t callback; 809 unsigned num_matches; 810 $malloc_match matches[2]; // Two items so they can be NULL terminated 811 void *ptr; 812} callback_baton_t; 813range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 814 callback_baton_t *info = (callback_baton_t *)baton; 815 if (info->num_matches == 0) { 816 uint8_t *p = (uint8_t *)info->ptr; 817 uint8_t *lo = (uint8_t *)ptr_addr; 818 uint8_t *hi = lo + ptr_size; 819 if (lo <= p && p < hi) { 820 info->matches[info->num_matches].addr = (void*)ptr_addr; 821 info->matches[info->num_matches].size = ptr_size; 822 info->matches[info->num_matches].offset = p - lo; 823 info->matches[info->num_matches].type = type; 824 info->num_matches = 1; 825 } 826 } 827}; 828callback_baton_t baton = { range_callback, 0, {0}, (void *)%s }; 829baton.matches[0].addr = 0; 830baton.matches[1].addr = 0;''' 831 if args: 832 total_matches = 0 833 for ptr_expr in args: 834 user_init_code = user_init_code_format % (ptr_expr) 835 expr = get_iterate_memory_expr(options, process, user_init_code, 'baton.matches') 836 arg_str_description = 'malloc block that contains %s' % ptr_expr 837 total_matches += display_match_results (result, options, arg_str_description, expr) 838 return total_matches 839 else: 840 result.AppendMessage('error: command takes one or more pointer expressions') 841 return 0 842 843def get_thread_stack_ranges_struct (process): 844 '''Create code that defines a structure that represents threads stack bounds 845 for all threads. It returns a static sized array initialized with all of 846 the tid, base, size structs for all the threads.''' 847 stack_dicts = list() 848 if process: 849 i = 0; 850 for thread in process: 851 min_sp = thread.frame[0].sp 852 max_sp = min_sp 853 for frame in thread.frames: 854 sp = frame.sp 855 if sp < min_sp: min_sp = sp 856 if sp > max_sp: max_sp = sp 857 if min_sp < max_sp: 858 stack_dicts.append ({ 'tid' : thread.GetThreadID(), 'base' : min_sp , 'size' : max_sp-min_sp, 'index' : i }) 859 i += 1 860 stack_dicts_len = len(stack_dicts) 861 if stack_dicts_len > 0: 862 result = ''' 863#define NUM_STACKS %u 864#define STACK_RED_ZONE_SIZE %u 865typedef struct thread_stack_t { uint64_t tid, base, size; } thread_stack_t; 866thread_stack_t stacks[NUM_STACKS];''' % (stack_dicts_len, process.target.GetStackRedZoneSize()) 867 for stack_dict in stack_dicts: 868 result += ''' 869stacks[%(index)u].tid = 0x%(tid)x; 870stacks[%(index)u].base = 0x%(base)x; 871stacks[%(index)u].size = 0x%(size)x;''' % stack_dict 872 return result 873 else: 874 return None 875 876def get_sections_ranges_struct (process): 877 '''Create code that defines a structure that represents all segments that 878 can contain data for all images in "target". It returns a static sized 879 array initialized with all of base, size structs for all the threads.''' 880 target = process.target 881 segment_dicts = list() 882 for (module_idx, module) in enumerate(target.modules): 883 for sect_idx in range(module.GetNumSections()): 884 section = module.GetSectionAtIndex(sect_idx) 885 if not section: 886 break 887 name = section.name 888 if name != '__TEXT' and name != '__LINKEDIT' and name != '__PAGEZERO': 889 base = section.GetLoadAddress(target) 890 size = section.GetByteSize() 891 if base != lldb.LLDB_INVALID_ADDRESS and size > 0: 892 segment_dicts.append ({ 'base' : base, 'size' : size }) 893 segment_dicts_len = len(segment_dicts) 894 if segment_dicts_len > 0: 895 result = ''' 896#define NUM_SEGMENTS %u 897typedef struct segment_range_t { uint64_t base; uint32_t size; } segment_range_t; 898segment_range_t segments[NUM_SEGMENTS];''' % (segment_dicts_len,) 899 for (idx, segment_dict) in enumerate(segment_dicts): 900 segment_dict['index'] = idx 901 result += ''' 902segments[%(index)u].base = 0x%(base)x; 903segments[%(index)u].size = 0x%(size)x;''' % segment_dict 904 return result 905 else: 906 return None 907 908def section_ptr_refs(debugger, command, result, dict): 909 command_args = shlex.split(command) 910 usage = "usage: %prog [options] <EXPR> [EXPR ...]" 911 description='''Searches section contents for pointer values in darwin user space programs.''' 912 parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage) 913 add_common_options(parser) 914 parser.add_option('--section', action='append', type='string', dest='section_names', help='section name to search', default=list()) 915 try: 916 (options, args) = parser.parse_args(command_args) 917 except: 918 return 919 920 options.type = 'pointer' 921 922 sections = list() 923 section_modules = list() 924 if not options.section_names: 925 result.AppendMessage('error: at least one section must be specified with the --section option') 926 return 927 928 target = lldb.debugger.GetSelectedTarget() 929 for module in target.modules: 930 for section_name in options.section_names: 931 section = module.section[section_name] 932 if section: 933 sections.append (section) 934 section_modules.append (module) 935 if sections: 936 dylid_load_err = load_dylib() 937 if dylid_load_err: 938 result.AppendMessage(dylid_load_err) 939 return 940 frame = target.GetProcess().GetSelectedThread().GetSelectedFrame() 941 for expr_str in args: 942 for (idx, section) in enumerate(sections): 943 expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (section.addr.load_addr, section.size, expr_str) 944 arg_str_description = 'section %s.%s containing "%s"' % (section_modules[idx].file.fullpath, section.name, expr_str) 945 num_matches = display_match_results (result, options, arg_str_description, expr, False) 946 if num_matches: 947 if num_matches < options.max_matches: 948 options.max_matches = options.max_matches - num_matches 949 else: 950 options.max_matches = 0 951 if options.max_matches == 0: 952 return 953 else: 954 result.AppendMessage('error: no sections were found that match any of %s' % (', '.join(options.section_names))) 955 956def get_objc_refs_options(): 957 usage = "usage: %prog [options] <CLASS> [CLASS ...]" 958 description='''Searches all allocations on the heap for instances of 959objective C classes, or any classes that inherit from the specified classes 960in darwin user space programs. Any matches that were found will dump the malloc 961blocks that contain the C strings and might be able to print what kind of 962objects the pointers are contained in using dynamic type information in the 963program.''' 964 parser = optparse.OptionParser(description=description, prog='objc_refs',usage=usage) 965 add_common_options(parser) 966 return parser 967 968def objc_refs(debugger, command, result, dict): 969 command_args = shlex.split(command) 970 parser = get_objc_refs_options() 971 try: 972 (options, args) = parser.parse_args(command_args) 973 except: 974 return 975 976 process = lldb.debugger.GetSelectedTarget().GetProcess() 977 if not process: 978 result.AppendMessage('error: invalid process') 979 return 980 frame = process.GetSelectedThread().GetSelectedFrame() 981 if not frame: 982 result.AppendMessage('error: invalid frame') 983 return 984 985 options.type = 'isa' 986 if options.format == None: 987 options.format = "A" # 'A' is "address" format 988 989 expr_options = lldb.SBExpressionOptions() 990 expr_options.SetIgnoreBreakpoints(True); 991 expr_options.SetTimeoutInMicroSeconds (3*1000*1000) # 3 second infinite timeout 992 expr_options.SetTryAllThreads (True) 993 num_objc_classes_value = frame.EvaluateExpression("(int)objc_getClassList((void *)0, (int)0)", expr_options) 994 if not num_objc_classes_value.error.Success(): 995 result.AppendMessage('error: %s' % num_objc_classes_value.error.GetCString()) 996 return 997 998 num_objc_classes = num_objc_classes_value.GetValueAsUnsigned() 999 if num_objc_classes == 0: 1000 result.AppendMessage('error: no objective C classes in program') 1001 return 1002 1003 if args: 1004 # When we initialize the expression, we must define any types that 1005 # we will need when looking at every allocation. We must also define 1006 # a type named callback_baton_t and make an instance named "baton" 1007 # and initialize it how ever we want to. The address of "baton" will 1008 # be passed into our range callback. callback_baton_t must contain 1009 # a member named "callback" whose type is "range_callback_t". This 1010 # will be used by our zone callbacks to call the range callback for 1011 # each malloc range. 1012 user_init_code_format = ''' 1013#define MAX_MATCHES %u 1014struct $malloc_match { 1015 void *addr; 1016 uintptr_t size; 1017 uintptr_t offset; 1018 uintptr_t type; 1019}; 1020typedef int (*compare_callback_t)(const void *a, const void *b); 1021typedef struct callback_baton_t { 1022 range_callback_t callback; 1023 compare_callback_t compare_callback; 1024 unsigned num_matches; 1025 $malloc_match matches[MAX_MATCHES]; 1026 void *isa; 1027 Class classes[%u]; 1028} callback_baton_t; 1029compare_callback_t compare_callback = [](const void *a, const void *b) -> int { 1030 Class a_ptr = *(Class *)a; 1031 Class b_ptr = *(Class *)b; 1032 if (a_ptr < b_ptr) return -1; 1033 if (a_ptr > b_ptr) return +1; 1034 return 0; 1035}; 1036range_callback_t range_callback = [](task_t task, void *baton, unsigned type, uintptr_t ptr_addr, uintptr_t ptr_size) -> void { 1037 callback_baton_t *info = (callback_baton_t *)baton; 1038 if (sizeof(Class) <= ptr_size) { 1039 Class *curr_class_ptr = (Class *)ptr_addr; 1040 Class *matching_class_ptr = (Class *)bsearch (curr_class_ptr, 1041 (const void *)info->classes, 1042 sizeof(info->classes)/sizeof(Class), 1043 sizeof(Class), 1044 info->compare_callback); 1045 if (matching_class_ptr) { 1046 bool match = false; 1047 if (info->isa) { 1048 Class isa = *curr_class_ptr; 1049 if (info->isa == isa) 1050 match = true; 1051 else { // if (info->objc.match_superclasses) { 1052 Class super = (Class)class_getSuperclass(isa); 1053 while (super) { 1054 if (super == info->isa) { 1055 match = true; 1056 break; 1057 } 1058 super = (Class)class_getSuperclass(super); 1059 } 1060 } 1061 } 1062 else 1063 match = true; 1064 if (match) { 1065 if (info->num_matches < MAX_MATCHES) { 1066 info->matches[info->num_matches].addr = (void*)ptr_addr; 1067 info->matches[info->num_matches].size = ptr_size; 1068 info->matches[info->num_matches].offset = 0; 1069 info->matches[info->num_matches].type = type; 1070 ++info->num_matches; 1071 } 1072 } 1073 } 1074 } 1075}; 1076callback_baton_t baton = { range_callback, compare_callback, 0, {0}, (void *)0x%x, {0} }; 1077int nc = (int)objc_getClassList(baton.classes, sizeof(baton.classes)/sizeof(Class)); 1078(void)qsort (baton.classes, sizeof(baton.classes)/sizeof(Class), sizeof(Class), compare_callback);''' 1079 # We must also define a snippet of code to be run that returns 1080 # the result of the expression we run. 1081 # Here we return NULL if our pointer was not found in any malloc blocks, 1082 # and we return the address of the matches array so we can then access 1083 # the matching results 1084 user_return_code = '''if (baton.num_matches < MAX_MATCHES) 1085 baton.matches[baton.num_matches].addr = 0; // Terminate the matches array 1086 baton.matches''' 1087 # Iterate through all of our ObjC class name arguments 1088 for class_name in args: 1089 addr_expr_str = "(void *)[%s class]" % class_name 1090 expr_options = lldb.SBExpressionOptions() 1091 expr_options.SetIgnoreBreakpoints(True); 1092 expr_options.SetTimeoutInMicroSeconds (1*1000*1000) # 1 second timeout 1093 expr_options.SetTryAllThreads (True) 1094 expr_sbvalue = frame.EvaluateExpression (addr_expr_str, expr_options) 1095 if expr_sbvalue.error.Success(): 1096 isa = expr_sbvalue.unsigned 1097 if isa: 1098 options.type = 'isa' 1099 result.AppendMessage('Searching for all instances of classes or subclasses of "%s" (isa=0x%x)' % (class_name, isa)) 1100 user_init_code = user_init_code_format % (options.max_matches, num_objc_classes, isa) 1101 expr = get_iterate_memory_expr(options, process, user_init_code, user_return_code) 1102 arg_str_description = 'objective C classes with isa 0x%x' % isa 1103 display_match_results (result, options, arg_str_description, expr) 1104 else: 1105 result.AppendMessage('error: Can\'t find isa for an ObjC class named "%s"' % (class_name)) 1106 else: 1107 result.AppendMessage('error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error)) 1108 else: 1109 result.AppendMessage('error: command takes one or more C string arguments'); 1110 1111if __name__ == '__main__': 1112 lldb.debugger = lldb.SBDebugger.Create() 1113 1114# Make the options so we can generate the help text for the new LLDB 1115# command line command prior to registering it with LLDB below. This way 1116# if clients in LLDB type "help malloc_info", they will see the exact same 1117# output as typing "malloc_info --help". 1118ptr_refs.__doc__ = get_ptr_refs_options().format_help() 1119cstr_refs.__doc__ = get_cstr_refs_options().format_help() 1120malloc_info.__doc__ = get_malloc_info_options().format_help() 1121objc_refs.__doc__ = get_objc_refs_options().format_help() 1122lldb.debugger.HandleCommand('command script add -f %s.ptr_refs ptr_refs' % __name__) 1123lldb.debugger.HandleCommand('command script add -f %s.cstr_refs cstr_refs' % __name__) 1124lldb.debugger.HandleCommand('command script add -f %s.malloc_info malloc_info' % __name__) 1125# lldb.debugger.HandleCommand('command script add -f %s.heap heap' % package_name) 1126# lldb.debugger.HandleCommand('command script add -f %s.section_ptr_refs section_ptr_refs' % package_name) 1127# lldb.debugger.HandleCommand('command script add -f %s.stack_ptr_refs stack_ptr_refs' % package_name) 1128lldb.debugger.HandleCommand('command script add -f %s.objc_refs objc_refs' % __name__) 1129print '"malloc_info", "ptr_refs", "cstr_refs", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.' 1130 1131 1132 1133 1134