From ae23ed336b1ffc496b2fdd537e05bc91273dca4d Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Mon, 8 Oct 2012 22:39:38 +0000 Subject: [PATCH] Checking in fixes that I used to track down a leaking module. The heap module can now search the vm regions with the --vm-regions options to any of the heap functions. This is currently slow and often will time out when run on a large program since our user expression timeout is set to 500000 usec. We need to add an API to LLDB where we can specify the timeout for an expression. llvm-svn: 165437 --- lldb/examples/darwin/heap_find/heap.py | 64 +++++++++- .../darwin/heap_find/heap/heap_find.cpp | 109 +++++++++++++++--- 2 files changed, 150 insertions(+), 23 deletions(-) diff --git a/lldb/examples/darwin/heap_find/heap.py b/lldb/examples/darwin/heap_find/heap.py index 0e59deb55f8d..8bd8cd9653b2 100644 --- a/lldb/examples/darwin/heap_find/heap.py +++ b/lldb/examples/darwin/heap_find/heap.py @@ -133,6 +133,7 @@ def add_common_options(parser): 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) parser.add_option('-M', '--max-matches', type='int', dest='max_matches', help='the maximum number of matches to print', default=256) parser.add_option('-O', '--offset', type='int', dest='offset', help='the matching data must be at this offset', default=-1) + parser.add_option('-V', '--vm-regions', action='store_true', dest='check_vm_regions', help='Also check the VM regions', default=False) def dump_stack_history_entry(result, stack_history_entry, idx): address = int(stack_history_entry.address) @@ -313,22 +314,22 @@ def heap_search(result, options, arg_str): print_no_matches = True arg_str_description = arg_str if options.type == 'pointer': - expr = 'find_pointer_in_heap((void *)%s)' % (arg_str) + expr = 'find_pointer_in_heap((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions) arg_str_description = 'malloc block containing pointer %s' % arg_str if options.format == None: options.format = "A" # 'A' is "address" format elif options.type == 'isa': - expr = 'find_objc_objects_in_memory ((void *)%s)' % (arg_str) + expr = 'find_objc_objects_in_memory ((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions) #result.AppendMessage ('expr -u0 -- %s' % expr) # REMOVE THIS LINE arg_str_description = 'objective C classes with isa %s' % arg_str options.offset = 0 if options.format == None: options.format = "A" # 'A' is "address" format elif options.type == 'cstr': - expr = 'find_cstring_in_heap("%s")' % arg_str + expr = 'find_cstring_in_heap("%s", (int)%u)' % (arg_str, options.check_vm_regions) arg_str_description = 'malloc block containing "%s"' % arg_str elif options.type == 'addr': - expr = 'find_block_for_address((void *)%s)' % arg_str + expr = 'find_block_for_address((void *)%s, (int)%u)' % (arg_str, options.check_vm_regions) arg_str_description = 'malloc block for %s' % arg_str elif options.type == 'all': expr = 'get_heap_info(1)' @@ -430,6 +431,56 @@ def heap(debugger, command, result, dict): else: heap_search (result, options, None) +def stack_ptr_refs(debugger, command, result, dict): + command_args = shlex.split(command) + usage = "usage: %prog [options] [EXPR ...]" + description='''Searches thread stack contents for pointer values in darwin user space programs.''' + parser = optparse.OptionParser(description=description, prog='section_ptr_refs',usage=usage) + add_common_options(parser) + try: + (options, args) = parser.parse_args(command_args) + except: + return + + options.type = 'pointer' + + stack_threads = list() + stack_bases = list() + stack_sizes = list() + for thread in lldb.process: + min_sp = thread.frame[0].sp + max_sp = min_sp + for frame in thread.frames: + sp = frame.sp + if sp < min_sp: min_sp = sp + if sp > max_sp: max_sp = sp + result.AppendMessage ('%s stack [%#x - %#x)' % (thread, min_sp, max_sp)) + if min_sp < max_sp: + stack_threads.append (thread) + stack_bases.append (min_sp) + stack_sizes.append (max_sp-min_sp) + + if stack_bases: + dylid_load_err = load_dylib() + if dylid_load_err: + result.AppendMessage(dylid_load_err) + return + for expr_str in args: + for (idx, stack_base) in enumerate(stack_bases): + stack_size = stack_sizes[idx] + expr = 'find_pointer_in_memory(0x%xllu, %ullu, (void *)%s)' % (stack_base, stack_size, expr_str) + arg_str_description = 'thead %s stack containing "%s"' % (stack_threads[idx], expr_str) + num_matches = display_match_results (result, options, arg_str_description, lldb.frame.EvaluateExpression (expr), False) + if num_matches: + if num_matches < options.max_matches: + options.max_matches = options.max_matches - num_matches + else: + options.max_matches = 0 + if options.max_matches == 0: + return + else: + result.AppendMessage('error: no thread stacks were found that match any of %s' % (', '.join(options.section_names))) + def section_ptr_refs(debugger, command, result, dict): command_args = shlex.split(command) usage = "usage: %prog [options] [EXPR ...]" @@ -443,7 +494,7 @@ def section_ptr_refs(debugger, command, result, dict): return options.type = 'pointer' - + sections = list() section_modules = list() if not options.section_names: @@ -520,8 +571,9 @@ lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cs lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.heap heap') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.section_ptr_refs section_ptr_refs') +lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.stack_ptr_refs stack_ptr_refs') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.objc_refs objc_refs') -print '"ptr_refs", "cstr_refs", "malloc_info", "heap" and "section_ptr_refs" commands have been installed, use the "--help" options on these commands for detailed help.' +print '"ptr_refs", "cstr_refs", "malloc_info", "heap", "section_ptr_refs" and "stack_ptr_refs" commands have been installed, use the "--help" options on these commands for detailed help.' diff --git a/lldb/examples/darwin/heap_find/heap/heap_find.cpp b/lldb/examples/darwin/heap_find/heap/heap_find.cpp index a24383b512fe..3b2b44cf6948 100644 --- a/lldb/examples/darwin/heap_find/heap/heap_find.cpp +++ b/lldb/examples/darwin/heap_find/heap/heap_find.cpp @@ -68,6 +68,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,8 @@ typedef struct { #define stack_logging_type_generic 1 #define stack_logging_type_alloc 2 #define stack_logging_type_dealloc 4 +// This bit is made up by this code +#define stack_logging_type_vm_region 8 //---------------------------------------------------------------------- // Redefine private function prototypes from @@ -130,6 +133,13 @@ __mach_stack_logging_frames_for_uniqued_stack ( extern "C" void *gdb_class_getClass (void *objc_class); +static void +range_info_callback (task_t task, + void *baton, + unsigned type, + uint64_t ptr_addr, + uint64_t ptr_size); + //---------------------------------------------------------------------- // Redefine private gloval variables prototypes from // "/usr/local/include/stack_logging.h" @@ -153,6 +163,7 @@ struct range_callback_info_t zone_callback_t *zone_callback; range_callback_t *range_callback; void *baton; + int check_vm_regions; }; enum data_type_t @@ -188,6 +199,7 @@ struct range_contains_data_callback_info_t }; uint32_t match_count; bool done; + bool unique; }; struct malloc_match @@ -195,6 +207,7 @@ struct malloc_match void *addr; intptr_t size; intptr_t offset; + uintptr_t type; }; struct malloc_stack_entry @@ -239,6 +252,7 @@ public: clear() { m_size = 0; + bzero (&m_entries, sizeof(m_entries)); } bool @@ -248,8 +262,17 @@ public: } void - push_back (const malloc_match& m) + push_back (const malloc_match& m, bool unique = false) { + if (unique) + { + // Don't add the entry if there is already a match for this address + for (uint32_t i=0; izone_callback (info, (const malloc_zone_t *)zones[i]); } } + + if (info->check_vm_regions) + { +#if defined (VM_REGION_SUBMAP_SHORT_INFO_COUNT_64) + typedef vm_region_submap_short_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 }; +#else + typedef vm_region_submap_info_data_64_t RegionInfo; + enum { kRegionInfoSize = VM_REGION_SUBMAP_INFO_COUNT_64 }; +#endif + task_t task = mach_task_self(); + mach_vm_address_t vm_region_base_addr; + mach_vm_size_t vm_region_size; + natural_t vm_region_depth; + RegionInfo vm_region_info; + + ((range_contains_data_callback_info_t *)info->baton)->unique = true; + + for (vm_region_base_addr = 0, vm_region_size = 1; vm_region_size != 0; vm_region_base_addr += vm_region_size) + { + mach_msg_type_number_t vm_region_info_size = kRegionInfoSize; + const kern_return_t err = mach_vm_region_recurse (task, + &vm_region_base_addr, + &vm_region_size, + &vm_region_depth, + (vm_region_recurse_info_t)&vm_region_info, + &vm_region_info_size); + if (err) + break; + // Check all read + write regions. This will cover the thread stacks + // and any regions of memory that aren't covered by the heap + if (vm_region_info.protection & VM_PROT_WRITE && + vm_region_info.protection & VM_PROT_READ) + { + //printf ("checking vm_region: [0x%16.16llx - 0x%16.16llx)\n", (uint64_t)vm_region_base_addr, (uint64_t)vm_region_base_addr + vm_region_size); + range_info_callback (task, + info->baton, + stack_logging_type_vm_region, + vm_region_base_addr, + vm_region_size); + } + } + } } //---------------------------------------------------------------------- @@ -635,8 +701,8 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, if (ptr_addr <= info->addr && info->addr < end_addr) { ++info->match_count; - malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr }; - g_matches.push_back(match); + malloc_match match = { (void *)ptr_addr, ptr_size, info->addr - ptr_addr, type }; + g_matches.push_back(match, info->unique); } break; @@ -659,8 +725,8 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, if (memcmp (buffer, ptr_data, size) == 0) { ++info->match_count; - malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr }; - g_matches.push_back(match); + malloc_match match = { (void *)ptr_addr, ptr_size, addr - ptr_addr, type }; + g_matches.push_back(match, info->unique); } } } @@ -714,8 +780,8 @@ range_info_callback (task_t task, void *baton, unsigned type, uint64_t ptr_addr, { //printf (" success\n"); ++info->match_count; - malloc_match match = { (void *)ptr_addr, ptr_size, 0 }; - g_matches.push_back(match); + malloc_match match = { (void *)ptr_addr, ptr_size, 0, type }; + g_matches.push_back(match, info->unique); } else { @@ -830,7 +896,7 @@ get_stack_history_for_address (const void * addr, int history) // blocks. //---------------------------------------------------------------------- malloc_match * -find_pointer_in_heap (const void * addr) +find_pointer_in_heap (const void * addr, int check_vm_regions) { g_matches.clear(); // Setup "info" to look for a malloc block that contains data @@ -844,8 +910,11 @@ find_pointer_in_heap (const void * addr) data_info.data.align = sizeof(addr); // Align to a pointer byte size data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop - range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info }; + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; foreach_zone_in_this_process (&info); + + } return g_matches.data(); } @@ -869,6 +938,7 @@ find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * data_info.data.align = sizeof(addr); // Align to a pointer byte size data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop + data_info.unique = false; // Set to true when iterating on the vm_regions range_info_callback (mach_task_self(), &data_info, stack_logging_type_generic, memory_addr, memory_size); return g_matches.data(); } @@ -881,7 +951,7 @@ find_pointer_in_memory (uint64_t memory_addr, uint64_t memory_size, const void * // inherit from 'c' //---------------------------------------------------------------------- malloc_match * -find_objc_objects_in_memory (void *isa) +find_objc_objects_in_memory (void *isa, int check_vm_regions) { g_matches.clear(); if (g_objc_classes.Update()) @@ -894,7 +964,8 @@ find_objc_objects_in_memory (void *isa) data_info.objc.match_superclasses = true; data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop - range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info }; + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; foreach_zone_in_this_process (&info); } return g_matches.data(); @@ -920,7 +991,9 @@ get_heap_info (int sort_type) data_info.type = eDataTypeHeapInfo; // Check each block for data data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop - range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info }; + data_info.unique = false; // Set to true when iterating on the vm_regions + const int check_vm_regions = false; + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; foreach_zone_in_this_process (&info); // Sort and print byte total bytes @@ -949,7 +1022,7 @@ get_heap_info (int sort_type) // Finds a C string inside one or more currently valid malloc blocks. //---------------------------------------------------------------------- malloc_match * -find_cstring_in_heap (const char *s) +find_cstring_in_heap (const char *s, int check_vm_regions) { g_matches.clear(); if (s == NULL || s[0] == '\0') @@ -966,7 +1039,8 @@ find_cstring_in_heap (const char *s) data_info.data.align = 1; // Data doesn't need to be aligned, so set the alignment to 1 data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop - range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info }; + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; foreach_zone_in_this_process (&info); return g_matches.data(); } @@ -977,7 +1051,7 @@ find_cstring_in_heap (const char *s) // Find the malloc block that whose address range contains "addr". //---------------------------------------------------------------------- malloc_match * -find_block_for_address (const void *addr) +find_block_for_address (const void *addr, int check_vm_regions) { g_matches.clear(); // Setup "info" to look for a malloc block that contains data @@ -987,7 +1061,8 @@ find_block_for_address (const void *addr) data_info.addr = (uintptr_t)addr; // What data? The C string passed in data_info.match_count = 0; // Initialize the match count to zero data_info.done = false; // Set done to false so searching doesn't stop - range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info }; + data_info.unique = false; // Set to true when iterating on the vm_regions + range_callback_info_t info = { enumerate_range_in_zone, range_info_callback, &data_info, check_vm_regions }; foreach_zone_in_this_process (&info); return g_matches.data(); }