More fixes to "malloc_history".

llvm-svn: 156605
This commit is contained in:
Greg Clayton 2012-05-11 02:42:36 +00:00
parent 74d7f15aed
commit 341039faca
2 changed files with 90 additions and 64 deletions

View File

@ -20,6 +20,7 @@ import lldb.utils.symbolication
g_libheap_dylib_dir = None g_libheap_dylib_dir = None
g_libheap_dylib_dict = dict() g_libheap_dylib_dict = dict()
g_verbose = False
def load_dylib(): def load_dylib():
if lldb.target: if lldb.target:
@ -72,6 +73,9 @@ def load_dylib():
return 'error: invalid target' return 'error: invalid target'
debugger.HandleCommand('process load "%s"' % libheap_dylib_path) debugger.HandleCommand('process load "%s"' % libheap_dylib_path)
if lldb.target.FindModule(libheap_dylib_spec):
return None # success, 'libheap.dylib' already loaded
return 'error: failed to load "%s"' % libheap_dylib_path
def add_common_options(parser): def add_common_options(parser):
parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False) parser.add_option('-v', '--verbose', action='store_true', dest='verbose', help='display verbose debug info', default=False)
@ -81,17 +85,25 @@ def add_common_options(parser):
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) 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)
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('-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)
type_flag_strings = [ 'free', 'generic', 'alloc', 'dealloc' ];
def dump_stack_history_entry(stack_history_entry, idx): def dump_stack_history_entry(stack_history_entry, idx):
address = int(stack_history_entry.address) address = int(stack_history_entry.address)
if address: if address:
type_flags = int(stack_history_entry.type_flags) type_flags = int(stack_history_entry.type_flags)
argument = int(stack_history_entry.argument)
symbolicator = lldb.utils.symbolication.Symbolicator() symbolicator = lldb.utils.symbolication.Symbolicator()
symbolicator.target = lldb.target symbolicator.target = lldb.target
global type_flag_strings type_str = ''
print 'stack_history_entry[%u]: addr = 0x%x, type=%s, arg=%u, frames=' % (idx, address, type_flag_strings[type_flags], argument) if type_flags == 0:
type_str = 'free'
else:
if type_flags & 2:
type_str = 'alloc'
elif type_flags & 4:
type_str = 'free'
elif type_flags & 1:
type_str = 'generic'
else:
type_str = hex(type_flags)
print 'stack[%u]: addr = 0x%x, type=%s, frames:' % (idx, address, type_str)
frame_idx = 0 frame_idx = 0
idx = 0 idx = 0
pc = int(stack_history_entry.frames[idx]) pc = int(stack_history_entry.frames[idx])
@ -100,20 +112,20 @@ def dump_stack_history_entry(stack_history_entry, idx):
frames = symbolicator.symbolicate(pc) frames = symbolicator.symbolicate(pc)
if frames: if frames:
for frame in frames: for frame in frames:
print '[%3u] %s' % (frame_idx, frame) print ' [%u] %s' % (frame_idx, frame)
frame_idx += 1 frame_idx += 1
else: else:
print '[%3u] 0x%x' % (frame_idx, pc) print ' [%u] 0x%x' % (frame_idx, pc)
frame_idx += 1 frame_idx += 1
idx = idx + 1 idx = idx + 1
pc = int(stack_history_entry.frames[idx]) pc = int(stack_history_entry.frames[idx])
else: else:
pc = 0 pc = 0
print
def dump_stack_history_entries(addr): def dump_stack_history_entries(addr, history):
# malloc_stack_entry *get_stack_history_for_address (const void * addr) # malloc_stack_entry *get_stack_history_for_address (const void * addr)
expr = 'get_stack_history_for_address((void *)0x%x, %u)' % (addr, history)
expr = 'get_stack_history_for_address((void *)0x%x)' % (addr)
print 'expr = "%s"' % (expr) print 'expr = "%s"' % (expr)
expr_sbvalue = lldb.frame.EvaluateExpression (expr) expr_sbvalue = lldb.frame.EvaluateExpression (expr)
if expr_sbvalue.error.Success(): if expr_sbvalue.error.Success():
@ -126,6 +138,10 @@ def dump_stack_history_entries(addr):
dump_stack_history_entry(stack_history_entry, idx) dump_stack_history_entry(stack_history_entry, idx)
idx = idx + 1 idx = idx + 1
stack_history_entry = expr_value[idx] stack_history_entry = expr_value[idx]
else:
print 'error: expression returned => %s' % (expr_sbvalue)
else:
print 'error: expression failed "%s" => %s' % (expr, expr_sbvalue.error)
def heap_search(options, arg_str): def heap_search(options, arg_str):
@ -234,42 +250,9 @@ def heap_search(options, arg_str):
lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result) lldb.debugger.GetCommandInterpreter().HandleCommand(memory_command, cmd_result)
print cmd_result.GetOutput() print cmd_result.GetOutput()
if options.stack_history: if options.stack_history:
dump_stack_history_entries(malloc_addr) dump_stack_history_entries(malloc_addr, 1)
elif options.stack: elif options.stack:
symbolicator = lldb.utils.symbolication.Symbolicator() dump_stack_history_entries(malloc_addr, 0)
symbolicator.target = lldb.target
expr_str = "g_stack_frames_count = sizeof(g_stack_frames)/sizeof(uint64_t); (int)__mach_stack_logging_get_frames((unsigned)mach_task_self(), 0x%xull, g_stack_frames, g_stack_frames_count, &g_stack_frames_count)" % (malloc_addr)
#print expr_str
expr = lldb.frame.EvaluateExpression (expr_str);
expr_error = expr.GetError()
if expr_error.Success():
err = expr.unsigned
if err:
print 'error: __mach_stack_logging_get_frames() returned error %i' % (err)
else:
count_expr = lldb.frame.EvaluateExpression ("g_stack_frames_count")
count = count_expr.unsigned
#print 'g_stack_frames_count is %u' % (count)
if count > 0:
frame_idx = 0
frames_expr = lldb.value(lldb.frame.EvaluateExpression ("g_stack_frames"))
done = False
for stack_frame_idx in range(count):
if not done:
frame_load_addr = int(frames_expr[stack_frame_idx])
if frame_load_addr >= 0x1000:
frames = symbolicator.symbolicate(frame_load_addr)
if frames:
for frame in frames:
print '[%3u] %s' % (frame_idx, frame)
frame_idx += 1
else:
print '[%3u] 0x%x' % (frame_idx, frame_load_addr)
frame_idx += 1
else:
done = True
else:
print 'error: %s' % (expr_error)
else: else:
print '%s %s was not found in any malloc blocks' % (options.type, arg_str) print '%s %s was not found in any malloc blocks' % (options.type, arg_str)
else: else:
@ -278,7 +261,7 @@ def heap_search(options, arg_str):
def ptr_refs(debugger, command, result, dict): def ptr_refs(debugger, command, result, dict):
command_args = shlex.split(command) command_args = shlex.split(command)
usage = "usage: %prog [options] <PTR> [PTR ...]" usage = "usage: %prog [options] <EXPR> [EXPR ...]"
description='''Searches the heap for pointer references on darwin user space programs. description='''Searches the heap for pointer references on darwin user space programs.
Any matches that were found will dump the malloc blocks that contain the pointers Any matches that were found will dump the malloc blocks that contain the pointers
@ -326,7 +309,7 @@ def cstr_refs(debugger, command, result, dict):
def malloc_info(debugger, command, result, dict): def malloc_info(debugger, command, result, dict):
command_args = shlex.split(command) command_args = shlex.split(command)
usage = "usage: %prog [options] <ADDR> [ADDR ...]" usage = "usage: %prog [options] <EXPR> [EXPR ...]"
description='''Searches the heap a malloc block that contains the addresses specified as arguments. description='''Searches the heap a malloc block that contains the addresses specified as arguments.
Any matches that were found will dump the malloc blocks that match or contain Any matches that were found will dump the malloc blocks that match or contain
@ -338,16 +321,37 @@ def malloc_info(debugger, command, result, dict):
(options, args) = parser.parse_args(command_args) (options, args) = parser.parse_args(command_args)
except: except:
return return
options.type = 'addr' options.type = 'addr'
if args: if args:
for data in args: for data in args:
heap_search (options, data) heap_search (options, data)
else: else:
print 'error: no c string arguments were given to search for' print 'error: no c string arguments were given to search for'
def malloc_history(debugger, command, result, dict):
command_args = shlex.split(command)
usage = "usage: %prog [options] <EXPR> [EXPR ...]"
description='''Gets the allocation history for an expression whose result is an address.
Programs should set the MallocStackLoggingNoCompact=1 in the environment to enable stack history. This can be done
with "process launch -v MallocStackLoggingNoCompact=1 -- [arg1 ...]"'''
dylid_load_err = load_dylib()
if dylid_load_err:
print dylid_load_err
else:
if command_args:
for addr_expr_str in command_args:
expr_sbvalue = lldb.frame.EvaluateExpression (addr_expr_str)
if expr_sbvalue.error.Success():
addr = expr_sbvalue.unsigned
if addr != 0:
dump_stack_history_entries (addr, 1)
else:
print 'error: expression error for "%s": %s' % (addr_expr_str, expr_sbvalue.error)
else:
print 'error: no address expressions were specified'
if __name__ == '__main__': if __name__ == '__main__':
lldb.debugger = lldb.SBDebugger.Create() lldb.debugger = lldb.SBDebugger.Create()
@ -356,7 +360,8 @@ if __name__ == '__main__':
lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.ptr_refs ptr_refs')
lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.cstr_refs cstr_refs')
lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info') lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_info malloc_info')
print '"ptr_refs", "cstr_refs", and "malloc_info" commands have been installed, use the "--help" options on these commands for detailed help.' lldb.debugger.HandleCommand('command script add -f lldb.macosx.heap.malloc_history malloc_history')
print '"ptr_refs", "cstr_refs", "malloc_info", and "malloc_history" commands have been installed, use the "--help" options on these commands for detailed help.'

View File

@ -121,7 +121,7 @@ struct malloc_match
struct malloc_stack_entry struct malloc_stack_entry
{ {
void *address; const void *address;
uint64_t argument; uint64_t argument;
uint32_t type_flags; uint32_t type_flags;
std::vector<uintptr_t> frames; std::vector<uintptr_t> frames;
@ -132,7 +132,7 @@ std::vector<malloc_match> g_matches;
const void *g_lookup_addr = 0; const void *g_lookup_addr = 0;
std::vector<malloc_stack_entry> g_malloc_stack_history; std::vector<malloc_stack_entry> g_malloc_stack_history;
mach_vm_address_t g_stack_frames[MAX_FRAMES]; mach_vm_address_t g_stack_frames[MAX_FRAMES];
uint32_t g_stack_frames_count = 0; char g_error_string[PATH_MAX];
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// task_peek // task_peek
@ -273,21 +273,42 @@ get_stack_for_address_enumerator(mach_stack_logging_record_t stack_record, void
} }
malloc_stack_entry * malloc_stack_entry *
get_stack_history_for_address (const void * addr) get_stack_history_for_address (const void * addr, int history)
{ {
// typedef struct {
// uint32_t type_flags;
// uint64_t stack_identifier;
// uint64_t argument;
// mach_vm_address_t address;
// } mach_stack_logging_record_t;
std::vector<malloc_stack_entry> empty; std::vector<malloc_stack_entry> empty;
g_malloc_stack_history.swap(empty); g_malloc_stack_history.swap(empty);
if (!stack_logging_enable_logging || (history && !stack_logging_dontcompact))
{
if (history)
strncpy(g_error_string, "error: stack history logging is not enabled, set MallocStackLoggingNoCompact=1 in the environment when launching to enable stack history logging.", sizeof(g_error_string));
else
strncpy(g_error_string, "error: stack logging is not enabled, set MallocStackLogging=1 in the environment when launching to enable stack logging.", sizeof(g_error_string));
return NULL;
}
kern_return_t err;
task_t task = mach_task_self(); task_t task = mach_task_self();
kern_return_t err = __mach_stack_logging_enumerate_records (task, if (history)
{
err = __mach_stack_logging_enumerate_records (task,
(mach_vm_address_t)addr, (mach_vm_address_t)addr,
get_stack_for_address_enumerator, get_stack_for_address_enumerator,
&task); &task);
}
else
{
uint32_t num_frames = 0;
err = __mach_stack_logging_get_frames(task, (mach_vm_address_t)addr, g_stack_frames, MAX_FRAMES, &num_frames);
if (err == 0 && num_frames > 0)
{
g_malloc_stack_history.resize(1);
g_malloc_stack_history.back().address = addr;
g_malloc_stack_history.back().type_flags = stack_logging_type_alloc;
g_malloc_stack_history.back().argument = 0;
if (num_frames > 0)
g_malloc_stack_history.back().frames.assign(g_stack_frames, g_stack_frames + num_frames);
g_malloc_stack_history.back().frames.push_back(0); // Terminate the frames with zero
}
}
// Append an empty entry // Append an empty entry
if (g_malloc_stack_history.empty()) if (g_malloc_stack_history.empty())
return NULL; return NULL;