diff options
author | Patrick Williams <iawillia@us.ibm.com> | 2012-08-22 17:18:45 -0500 |
---|---|---|
committer | A. Patrick Williams III <iawillia@us.ibm.com> | 2012-09-07 10:13:22 -0500 |
commit | 8fdce540fed17630af2fe1412c963167e0a63a9c (patch) | |
tree | baefefaf8a330b0fffead8f693cd96a356328413 | |
parent | 4af5b97526d99ecb9e232b1933c804858a298876 (diff) | |
download | talos-hostboot-8fdce540fed17630af2fe1412c963167e0a63a9c.tar.gz talos-hostboot-8fdce540fed17630af2fe1412c963167e0a63a9c.zip |
Memory profiling tools.
Change-Id: I4a9cfc1f55c09ff6d02dce80889ac2e3150ab137
RTC: 44504
Reviewed-on: http://gfw160.austin.ibm.com:8080/gerrit/1619
Tested-by: Jenkins Server
Reviewed-by: Douglas R. Gilbert <dgilbert@us.ibm.com>
Reviewed-by: Daniel M. Crowell <dcrowell@us.ibm.com>
Reviewed-by: A. Patrick Williams III <iawillia@us.ibm.com>
-rwxr-xr-x | src/build/debug/memalloc.pl | 270 | ||||
-rwxr-xr-x | src/build/debug/simics-debug-framework.py | 101 | ||||
-rw-r--r-- | src/include/arch/ppc.H | 3 | ||||
-rw-r--r-- | src/lib/makefile | 48 | ||||
-rw-r--r-- | src/lib/stdlib.C | 108 |
5 files changed, 468 insertions, 62 deletions
diff --git a/src/build/debug/memalloc.pl b/src/build/debug/memalloc.pl new file mode 100755 index 000000000..9a376c4dc --- /dev/null +++ b/src/build/debug/memalloc.pl @@ -0,0 +1,270 @@ +#!/usr/bin/perl +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/build/debug/memalloc.pl $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2012 +# +# p1 +# +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code +# +# The source code for this program is not published or other- +# wise divested of its trade secrets, irrespective of what has +# been deposited with the U.S. Copyright Office. +# +# Origin: 30 +# +# IBM_PROLOG_END_TAG + +# Usage: +# In order to use this tooling, you must recompile at least the src/lib +# directory with the environment variable HOSTBOOT_MEMORY_LEAKS=1. +# You need to 'make clean' to ensure this variable takes effect. +# +# The resulting image can be executed in simics, which will cause magic +# instructions to be triggered on each memory allocation (malloc, realloc, +# free). The simics debug tooling will catch the haps from the magic +# instructions and create a data file 'hb_memoryleak.dat'. This file +# can be fed to this script to analyse leaks or all memory analysis. +# +# If you desire to do memory analysis of just a certain portion of +# Hostboot execution (ex. a single istep), you may delete the +# 'hb_memoryleak.dat' file while simics is paused. +# + +use strict; + +# Parse parameters. +if ($#ARGV < 0) +{ + die "memalloc.pl [--all] <datafile>\n"; +} + +my $all_allocations = 0; +my $file = shift; +if ($file eq "--all") +{ + $all_allocations = 1; + $file = shift; +} + +# Open data file and parse. +open (MEMORY_LOG, "< $file") or die "Cannot open $file.\n"; + +my $lines = []; + +while(my $line = <MEMORY_LOG>) +{ + # Format: FUNCTION 0dSIZE 0xPTR1 0xPTR2 [ 0xLR 0xLR ... ] + if ($line =~ m/([A-Z]*) ([0-9]*) (0x[0-9a-f]*) (0x[0-9a-f]*) \[(.*)\]/) + { + my $parsed = {}; + $parsed->{"type"} = $1; + $parsed->{"size"} = int $2; + $parsed->{"ptr"} = hex $3; + $parsed->{"ptr2"} = hex $4; + + # Parse stack frame addresses. + my $stack_addrs = []; + my $stack = $5; + $parsed->{"stack_str"} = $stack; + while ($stack =~ m/(0x[0-9a-f]+) (.*)/) + { + push @{$stack_addrs}, (hex $1); + $stack = $2; + } + $parsed->{"stack"} = $stack_addrs; + push @{$lines}, $parsed; + } +} +close MEMORY_LOG; + +# Parse symbol file. +my %symbol_address = (); +my %symbol_isfunc = (); + +my $gensyms = $ENV{"HOSTBOOTROOT"}."/img/hbicore_test.syms"; +open (GENSYMS, "< $gensyms") or die "Cannot find syms file: $gensyms\n"; +while (my $line = <GENSYMS>) +{ + chomp $line; + my ($is_func,$code_addr,$addr,$function); + + $line =~ m/(.*?),(.*?),(.*?),(.*?),(.*)/; + $is_func = "F" eq $1; + $addr = hex $2; + $function = $5; + + if (not defined $symbol_address{$addr}) + { + $symbol_address{$addr} = (); + } + push @{$symbol_address{$addr}}, $function; + $symbol_isfunc{$function} = $is_func; +} +my @symbol_sorted_addrs = sort { $a <=> $b} keys %symbol_address; +# End parse symbol file. + + +# Filter out malloc/free pairs. +if (not $all_allocations) +{ + $lines = leaks_only($lines); +} + +# Display total memory usage (or leaks). +my $size = 0; +foreach my $line (@{$lines}) +{ + $size += $line->{"size"}; +} +print "Total Memory: ".$size." bytes\n"; + +# Group allocations with the same stack back-trace. +my %stacks = (); +foreach my $line (@{$lines}) +{ + next if ($line->{"type"} ne "MALLOC"); + + if (defined $stacks{$line->{"stack_str"}}) + { + $stacks{$line->{"stack_str"}}->{"size"} += $line->{"size"}; + $stacks{$line->{"stack_str"}}->{"blocks"} += 1; + } + else + { + my $stack_loc = {}; + $stack_loc->{"blocks"} = 1; + $stack_loc->{"size"} = $line->{"size"}; + $stack_loc->{"stack"} = $line->{"stack"}; + $stacks{$line->{"stack_str"}} = $stack_loc; + } +} + +# Display all stacks (and their memory allocations). +foreach my $stack (sort {$stacks{$b}->{"size"} <=> $stacks{$a}->{"size"}} + (keys %stacks)) +{ + print "-------------------------------------------------------\n"; + print $stacks{$stack}->{"blocks"}." blocks for a total of "; + print $stacks{$stack}->{"size"}." bytes.\n"; + foreach my $addr (@{$stacks{$stack}->{"stack"}}) + { + print (sprintf "\t%s (0x%x)\n", + find_symbol_name($addr, 0, \%symbol_address, + \@symbol_sorted_addrs, + \%symbol_isfunc), + $addr + ); + } +} + +# Function: leaks_only +# Brief: Filters out malloc / free pairs with the same address. +sub leaks_only +{ + my $allocs = {}; + my $lines = shift; + + foreach my $line (@{$lines}) + { + if ($line->{"type"} eq "MALLOC") + { + $allocs->{$line->{"ptr"}} = $line; + } + elsif ($line->{"type"} eq "REALLOC") + { + undef $allocs->{$line->{"ptr2"}}; + $allocs->{$line->{"ptr"}} = $line; + } + elsif ($line->{"type"} eq "FREE") + { + undef $allocs->{$line->{"ptr"}}; + } + } + + my @values = values %{$allocs}; + + return \@values; +} + +# Function: find_symbol_name +# Brief: Determines an appropriate symbol name based on the syms file and +# an address. (code came from genlist) +sub find_symbol_name +{ + my ($offset, $require_function, $symbol_addrs, + $symbol_sorted_addrs, $symbol_funcs) = @_; + + if (defined $symbol_addrs->{$offset}) + { + for my $sym (@{$symbol_addrs->{$offset}}) + { + if ($symbol_funcs->{$sym}) + { + return $sym; + } + } + if ($require_function) + { + return 0; + } + return @{$symbol_addrs->{$offset}}[0]; + } + if ($require_function) + { + return 0; + } + + my $prevoffset = -1; + my $search_first = 0; + my $search_last = $#$symbol_sorted_addrs; + while ($search_first != $search_last) + { + my $search_mid = int ($search_first + $search_last) / 2; + if ($search_mid == $search_first) + { + if (@$symbol_sorted_addrs[$search_last] <= $offset) + { + $search_first = $search_last; + } + else + { + $search_last = $search_first; + } + } + elsif (@$symbol_sorted_addrs[$search_mid] <= $offset) + { + $search_first = $search_mid; + } + else + { + $search_last = $search_mid; + } + } + if (@$symbol_sorted_addrs[$search_first] <= $offset) + { + $prevoffset = @$symbol_sorted_addrs[$search_first]; + } + + if (defined $symbol_addrs->{$prevoffset}) + { + for my $sym (@{$symbol_addrs->{$prevoffset}}) + { + if ($symbol_funcs->{$sym}) + { + return sprintf "%s+0x%x", $sym, ($offset - $prevoffset); + } + } + return sprintf "%s+0x%x", @{$symbol_addrs->{$prevoffset}}[0], + ($offset - $prevoffset); + } + return sprintf "Unknown @ 0x%x", $offset; +} + diff --git a/src/build/debug/simics-debug-framework.py b/src/build/debug/simics-debug-framework.py index 3ddbcd388..3f54263a6 100755 --- a/src/build/debug/simics-debug-framework.py +++ b/src/build/debug/simics-debug-framework.py @@ -1,26 +1,26 @@ #!/usr/bin/python -# IBM_PROLOG_BEGIN_TAG -# This is an automatically generated prolog. +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. # -# $Source: src/build/debug/simics-debug-framework.py $ +# $Source: src/build/debug/simics-debug-framework.py $ # -# IBM CONFIDENTIAL +# IBM CONFIDENTIAL # -# COPYRIGHT International Business Machines Corp. 2011-2012 +# COPYRIGHT International Business Machines Corp. 2011,2012 # -# p1 +# p1 # -# Object Code Only (OCO) source materials -# Licensed Internal Code Source Materials -# IBM HostBoot Licensed Internal Code +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code # -# The source code for this program is not published or other- -# wise divested of its trade secrets, irrespective of what has -# been deposited with the U.S. Copyright Office. +# The source code for this program is not published or otherwise +# divested of its trade secrets, irrespective of what has been +# deposited with the U.S. Copyright Office. # -# Origin: 30 +# Origin: 30 # -# IBM_PROLOG_END_TAG +# IBM_PROLOG_END_TAG # @file simics-debug-framework.py # @brief Simics/Python implementation of the common debug framework. # @@ -368,10 +368,80 @@ def intToList(n,size): n = n >> 8 return lst +# Convert a byte list to an integer. +def listToInt(l): + i = 0; + for c in l: + i = (i << 8) | c + return i + # Write the 64-bit big endian n at the address given. def writeLongLong(address,n): writeSimicsMemory(address,intToList(n,8)) +# Recursively parse out the saved link-registers in a stack. +# Param - cpu - CPU object to read stack from. +# Param - frame - Pointer to the frame-pointer to be parsed. +def magic_memoryleak_stackdump(cpu, frame): + + if frame == 0: + return [] + + # Pointer to the next frame is at the current frame memory address. + next_frame = \ + cpu.iface.processor_info.logical_to_physical(frame, 1).address + next_frame = listToInt( \ + conf.system_cmp0.phys_mem.memory[[next_frame, next_frame+7]]) + + if next_frame == 0: + return [] + + # The LR save area is 2 words ahead of the current frame pointer. + lr_save = \ + cpu.iface.processor_info.logical_to_physical(frame + 16, 1).address + lr_save = listToInt( \ + conf.system_cmp0.phys_mem.memory[[lr_save, lr_save+7]]) + + # Recursively add LR to rest of the stack-frame. + return [lr_save] + magic_memoryleak_stackdump(cpu, next_frame) + +# Respond to the magic instruction for a memory allocation function call. +# Param - cpu - The CPU raising the magic instruction. +# +# Registers: +# cpu.r3 - function called (see MemoryLeak_FunctionType). +# cpu.r4 - size of allocation (for malloc / realloc). +# cpu.r5 - pointer (result for malloc / realloc, parameter for free). +# cpu.r6 - pointer2 (original pointer for realloc). +# +def magic_memoryleak_function(cpu): + # Parse registers. + function = ["MALLOC", "REALLOC", "FREE"][cpu.r3] + size = cpu.r4 + ptr = cpu.r5 + ptr2 = cpu.r6 + + # Find stack frame. + stack_frame = \ + cpu.iface.processor_info.logical_to_physical(cpu.r1, 1).address + stack_frame = listToInt( \ + conf.system_cmp0.phys_mem.memory[[stack_frame, stack_frame+7]]) + + file = open("hb_memoryleak.dat", "a") + + # Output parameters. + file.write("%s %d 0x%x 0x%x" % (function,size,ptr,ptr2)) + # Output stack backtrace. + file.write(" [ %s ]\n" % str.join(" ", \ + ("0x%x" % i for i in magic_memoryleak_stackdump(cpu, stack_frame)))) + + file.close() + +# Erase the hb_memoryleak.dat save data when starting up. +try: + os.remove("hb_memoryleak.dat") +except: + 1 # MAGIC_INSTRUCTION hap handler # arg contains the integer parameter n passed to MAGIC_INSTRUCTION(n) @@ -394,6 +464,9 @@ def magic_instruction_callback(user_arg, cpu, arg): # Stop the simulation, much like a hard-coded breakpoint SIM_break_simulation( "Simulation stopped. (hap 7007)" ) + if arg == 7009: # MAGIC_MEMORYLEAK_FUNCTION + magic_memoryleak_function(cpu) + if arg == 7055: # MAGIC_CONTINUOUS_TRACE # Set execution environment flag to 0 writeLongLong(contTraceTrigInfo+32,0) diff --git a/src/include/arch/ppc.H b/src/include/arch/ppc.H index 42ec52db4..cb1ff4fd5 100644 --- a/src/include/arch/ppc.H +++ b/src/include/arch/ppc.H @@ -365,8 +365,9 @@ enum MAGIC_SHUTDOWN = 7006, // KernelMisc::shutdown() called. MAGIC_BREAK = 7007, // hard-code a breakpoint - MAGIC_CONTINUOUS_TRACE = 7055, // extract mixed trace buffer MAGIC_RANDOM = 7008, // generate random number + MAGIC_MEMORYLEAK_FUNCTION = 7009, // A memory was function called. + MAGIC_CONTINUOUS_TRACE = 7055, // extract mixed trace buffer }; diff --git a/src/lib/makefile b/src/lib/makefile index b7cd3196e..4b1d006db 100644 --- a/src/lib/makefile +++ b/src/lib/makefile @@ -1,25 +1,25 @@ -# IBM_PROLOG_BEGIN_TAG -# This is an automatically generated prolog. -# -# $Source: src/lib/makefile $ -# -# IBM CONFIDENTIAL -# -# COPYRIGHT International Business Machines Corp. 2010 - 2011 -# -# p1 -# -# Object Code Only (OCO) source materials -# Licensed Internal Code Source Materials -# IBM HostBoot Licensed Internal Code -# -# The source code for this program is not published or other- -# wise divested of its trade secrets, irrespective of what has -# been deposited with the U.S. Copyright Office. -# -# Origin: 30 -# -# IBM_PROLOG_END +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/makefile $ +# +# IBM CONFIDENTIAL +# +# COPYRIGHT International Business Machines Corp. 2010,2012 +# +# p1 +# +# Object Code Only (OCO) source materials +# Licensed Internal Code Source Materials +# IBM HostBoot Licensed Internal Code +# +# The source code for this program is not published or otherwise +# divested of its trade secrets, irrespective of what has been +# deposited with the U.S. Copyright Office. +# +# Origin: 30 +# +# IBM_PROLOG_END_TAG ROOTPATH = ../.. OBJS = string.o string_ext.o stdlib.o ctype.o assert.o stdio.o math.o @@ -27,4 +27,8 @@ OBJS += syscall_stub.o syscall_task.o syscall_msg.o OBJS += syscall_mmio.o syscall_time.o sync.o syscall_misc.o OBJS += syscall_mm.o splaytree.o cxxtest_data.o +ifdef HOSTBOOT_MEMORY_LEAKS +EXTRACOMMONFLAGS += -DHOSTBOOT_MEMORY_LEAKS=1 +endif + include ${ROOTPATH}/config.mk diff --git a/src/lib/stdlib.C b/src/lib/stdlib.C index f0c3fad74..50d776cd5 100644 --- a/src/lib/stdlib.C +++ b/src/lib/stdlib.C @@ -1,25 +1,25 @@ -// IBM_PROLOG_BEGIN_TAG -// This is an automatically generated prolog. -// -// $Source: src/lib/stdlib.C $ -// -// IBM CONFIDENTIAL -// -// COPYRIGHT International Business Machines Corp. 2010 - 2011 -// -// p1 -// -// Object Code Only (OCO) source materials -// Licensed Internal Code Source Materials -// IBM HostBoot Licensed Internal Code -// -// The source code for this program is not published or other- -// wise divested of its trade secrets, irrespective of what has -// been deposited with the U.S. Copyright Office. -// -// Origin: 30 -// -// IBM_PROLOG_END +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/stdlib.C $ */ +/* */ +/* IBM CONFIDENTIAL */ +/* */ +/* COPYRIGHT International Business Machines Corp. 2010,2012 */ +/* */ +/* p1 */ +/* */ +/* Object Code Only (OCO) source materials */ +/* Licensed Internal Code Source Materials */ +/* IBM HostBoot Licensed Internal Code */ +/* */ +/* The source code for this program is not published or otherwise */ +/* divested of its trade secrets, irrespective of what has been */ +/* deposited with the U.S. Copyright Office. */ +/* */ +/* Origin: 30 */ +/* */ +/* IBM_PROLOG_END_TAG */ #include <limits.h> #include <stdlib.h> #include <string.h> @@ -27,6 +27,46 @@ #include <kernel/pagemgr.H> #include <kernel/console.H> +#ifdef HOSTBOOT_MEMORY_LEAKS +#include <arch/ppc.H> + +/** Memory allocation function type + * + * These are used as parameters to the magic instruction so that the debug + * tools can determine what memory allocation function was being called. + */ +enum MemoryLeak_FunctionType +{ + MEMORYLEAK_MALLOC = 0, + MEMORYLEAK_REALLOC = 1, + MEMORYLEAK_FREE = 2 +}; + +/** @fn memoryleak_magic_instruction + * @brief Triggers the simics memoryleak analysis magic hap-handler. + * + * Arranges the memory allocation parameters into registers according to the + * Power ABI and triggers the magic instruction. The ABI puts parameter 0-3 + * into registers r3-r6. + * + * Function attribute of "noinline" is required to ensure that the compiler + * treats this as a real function instead of attempting to inline it. If it + * were to inline it then the parameters wouldn't end up in the right register. + */ +static void memoryleak_magic_instruction(MemoryLeak_FunctionType func, + size_t size, + void* ptr, + void* ptr2) __attribute__((noinline)); + +static void memoryleak_magic_instruction(MemoryLeak_FunctionType func, + size_t size, + void* ptr, + void* ptr2) +{ + MAGIC_INSTRUCTION(MAGIC_MEMORYLEAK_FUNCTION); + return; +} +#endif #ifdef MEM_ALLOC_PROFILE // alloc profile @@ -58,7 +98,14 @@ void* malloc(size_t s) else if (s <= 2048) ++g_2k; else ++g_big; #endif - return HeapManager::allocate(s); + + void* result = HeapManager::allocate(s); + +#ifdef HOSTBOOT_MEMORY_LEAKS + memoryleak_magic_instruction(MEMORYLEAK_MALLOC, s, result, NULL); +#endif + + return result; } @@ -66,6 +113,10 @@ void free(void* p) { if (NULL == p) return; +#ifdef HOSTBOOT_MEMORY_LEAKS + memoryleak_magic_instruction(MEMORYLEAK_FREE, 0, p, NULL); +#endif + HeapManager::free(p); } @@ -74,8 +125,14 @@ void* realloc(void* p, size_t s) { if (NULL == p) return malloc(s); - return HeapManager::realloc(p,s); -} + void* result = HeapManager::realloc(p,s); + +#ifdef HOSTBOOT_MEMORY_LEAKS + memoryleak_magic_instruction(MEMORYLEAK_REALLOC, s, result, p); +#endif + + return result; +} void* calloc(size_t num, size_t size) { @@ -92,3 +149,4 @@ void* calloc(size_t num, size_t size) return mem; } + |