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