summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPatrick Williams <iawillia@us.ibm.com>2012-08-22 17:18:45 -0500
committerA. Patrick Williams III <iawillia@us.ibm.com>2012-09-07 10:13:22 -0500
commit8fdce540fed17630af2fe1412c963167e0a63a9c (patch)
treebaefefaf8a330b0fffead8f693cd96a356328413 /src
parent4af5b97526d99ecb9e232b1933c804858a298876 (diff)
downloadtalos-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>
Diffstat (limited to 'src')
-rwxr-xr-xsrc/build/debug/memalloc.pl270
-rwxr-xr-xsrc/build/debug/simics-debug-framework.py101
-rw-r--r--src/include/arch/ppc.H3
-rw-r--r--src/lib/makefile48
-rw-r--r--src/lib/stdlib.C108
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;
}
+
OpenPOWER on IntegriCloud