summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2007-03-22 04:42:30 +0000
committercsilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50>2007-03-22 04:42:30 +0000
commitbc455d7b63949fab94ed9518d277866e95f08768 (patch)
tree50e71170b8d89cece878f7f337cc9abcbc8d4eed
parent91fad389784766782263133c5510976a8f76d89e (diff)
downloadgperftools-bc455d7b63949fab94ed9518d277866e95f08768.tar.gz
Fri Jun 24 18:02:26 2005 Google Inc. <opensource@google.com>
* Add missing errno include for one of the unittests (csilvers) * Reduce tcmalloc startup memory from 5M to 256K (sanjay) * Add support for mallopt() and mallinfo (sanjay) * Improve stacktrace's performance on some 64-bit systems (etune) * Improve the stacktrace unittest (etune) git-svn-id: http://gperftools.googlecode.com/svn/trunk@13 6b5cf1ce-ec42-a296-1ba9-69fdba395a50
-rw-r--r--ChangeLog8
-rwxr-xr-xconfigure20
-rw-r--r--configure.ac2
-rw-r--r--src/pagemap.h60
-rw-r--r--src/stacktrace.cc7
-rw-r--r--src/tcmalloc.cc121
-rw-r--r--src/tests/heap-checker_unittest.cc1
-rw-r--r--src/tests/stacktrace_unittest.cc71
8 files changed, 235 insertions, 55 deletions
diff --git a/ChangeLog b/ChangeLog
index d5f9176..b8391f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,3 +24,11 @@ Tue May 31 08:14:38 2005 Google Inc. <opensource@google.com>
* Better error reporting when we can't access /proc/maps (etune)
* Get rid of the libc-preallocate code (which could crash on some
systems); no longer needed with local-threads fix (csilvers)
+
+Fri Jun 24 18:02:26 2005 Google Inc. <opensource@google.com>
+
+ * Add missing errno include for one of the unittests (csilvers)
+ * Reduce tcmalloc startup memory from 5M to 256K (sanjay)
+ * Add support for mallopt() and mallinfo (sanjay)
+ * Improve stacktrace's performance on some 64-bit systems (etune)
+ * Improve the stacktrace unittest (etune)
diff --git a/configure b/configure
index 13dc54a..421ada1 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.57 for google-perftools 0.2.
+# Generated by GNU Autoconf 2.57 for google-perftools 0.3.
#
# Report bugs to <opensource@google.com>.
#
@@ -422,8 +422,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='google-perftools'
PACKAGE_TARNAME='google-perftools'
-PACKAGE_VERSION='0.2'
-PACKAGE_STRING='google-perftools 0.2'
+PACKAGE_VERSION='0.3'
+PACKAGE_STRING='google-perftools 0.3'
PACKAGE_BUGREPORT='opensource@google.com'
ac_unique_file="README"
@@ -953,7 +953,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures google-perftools 0.2 to adapt to many kinds of systems.
+\`configure' configures google-perftools 0.3 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1019,7 +1019,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of google-perftools 0.2:";;
+ short | recursive ) echo "Configuration of google-perftools 0.3:";;
esac
cat <<\_ACEOF
@@ -1125,7 +1125,7 @@ fi
test -n "$ac_init_help" && exit 0
if $ac_init_version; then
cat <<\_ACEOF
-google-perftools configure 0.2
+google-perftools configure 0.3
generated by GNU Autoconf 2.57
Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002
@@ -1140,7 +1140,7 @@ cat >&5 <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by google-perftools $as_me 0.2, which was
+It was created by google-perftools $as_me 0.3, which was
generated by GNU Autoconf 2.57. Invocation command line was
$ $0 $@
@@ -1733,7 +1733,7 @@ fi
# Define the identity of the package.
PACKAGE=google-perftools
- VERSION=0.2
+ VERSION=0.3
cat >>confdefs.h <<_ACEOF
@@ -22015,7 +22015,7 @@ _ASBOX
} >&5
cat >&5 <<_CSEOF
-This file was extended by google-perftools $as_me 0.2, which was
+This file was extended by google-perftools $as_me 0.3, which was
generated by GNU Autoconf 2.57. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -22078,7 +22078,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
-google-perftools config.status 0.2
+google-perftools config.status 0.3
configured by $0, generated by GNU Autoconf 2.57,
with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
diff --git a/configure.ac b/configure.ac
index 0226ef5..245d166 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@
# make sure we're interpreted by some minimal autoconf
AC_PREREQ(2.57)
-AC_INIT(google-perftools, 0.2, opensource@google.com)
+AC_INIT(google-perftools, 0.3, opensource@google.com)
# The argument here is just something that should be in the current directory
# (for sanity checking)
AC_CONFIG_SRCDIR(README)
diff --git a/src/pagemap.h b/src/pagemap.h
index 326d970..50ff1bf 100644
--- a/src/pagemap.h
+++ b/src/pagemap.h
@@ -94,6 +94,66 @@ class TCMalloc_PageMap1 {
}
};
+// Two-level radix tree
+template <int BITS>
+class TCMalloc_PageMap2 {
+ private:
+ // Put 32 entries in the root and (2^BITS)/32 entries in each leaf.
+ static const int ROOT_BITS = 5;
+ static const int ROOT_LENGTH = 1 << ROOT_BITS;
+
+ static const int LEAF_BITS = BITS - ROOT_BITS;
+ static const int LEAF_LENGTH = 1 << LEAF_BITS;
+
+ // Leaf node
+ struct Leaf {
+ void* values[LEAF_LENGTH];
+ };
+
+ Leaf* root_[ROOT_LENGTH]; // Pointers to 32 child nodes
+ void* (*allocator_)(size_t); // Memory allocator
+
+ public:
+ typedef uintptr_t Number;
+
+ explicit TCMalloc_PageMap2(void* (*allocator)(size_t)) {
+ allocator_ = allocator;
+ memset(root_, 0, sizeof(root_));
+ }
+
+ void* get(Number k) const {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> LEAF_BITS;
+ const Number i2 = k & (LEAF_LENGTH-1);
+ return root_[i1]->values[i2];
+ }
+
+ void set(Number k, void* v) {
+ ASSERT(k >> BITS == 0);
+ const Number i1 = k >> LEAF_BITS;
+ const Number i2 = k & (LEAF_LENGTH-1);
+ root_[i1]->values[i2] = v;
+ }
+
+ bool Ensure(Number start, size_t n) {
+ for (Number key = start; key <= start + n - 1; ) {
+ const Number i1 = key >> LEAF_BITS;
+
+ // Make 2nd level node if necessary
+ if (root_[i1] == NULL) {
+ Leaf* leaf = reinterpret_cast<Leaf*>((*allocator_)(sizeof(Leaf)));
+ if (leaf == NULL) return false;
+ memset(leaf, 0, sizeof(*leaf));
+ root_[i1] = leaf;
+ }
+
+ // Advance key past whatever is covered by this leaf node
+ key = ((key >> LEAF_BITS) + 1) << LEAF_BITS;
+ }
+ return true;
+ }
+};
+
// Three-level radix tree
template <int BITS>
class TCMalloc_PageMap3 {
diff --git a/src/stacktrace.cc b/src/stacktrace.cc
index 07fd321..dcc8bc3 100644
--- a/src/stacktrace.cc
+++ b/src/stacktrace.cc
@@ -40,8 +40,8 @@
// Linux/x86 implementation (requires the binary to be compiled with
// frame pointers)
-#if (defined(__i386__) || defined(__x86_64)) && \
- defined(__linux) && !defined(NO_FRAME_POINTER) && !defined(_LP64)
+#if (defined(__i386__) || defined(__x86_64__)) && \
+ defined(__linux) && !defined(NO_FRAME_POINTER)
#define IMPLEMENTED_STACK_TRACE
#include <stdint.h> // for uintptr_t
@@ -129,6 +129,9 @@ bool GetStackExtent(void* sp, void** stack_top, void** stack_bottom) {
#endif
// Portable implementation - just use glibc
+//
+// Note: The glibc implementation may cause a call to malloc.
+// This can cause a deadlock in HeapProfiler.
#if !defined(IMPLEMENTED_STACK_TRACE) && defined(HAVE_EXECINFO_H)
#include <stdlib.h>
#include <execinfo.h>
diff --git a/src/tcmalloc.cc b/src/tcmalloc.cc
index 80bb6d9..748f8f5 100644
--- a/src/tcmalloc.cc
+++ b/src/tcmalloc.cc
@@ -307,12 +307,12 @@ template <class T>
class PageHeapAllocator {
private:
// How much to allocate from system at a time
- static const int kAllocIncrement = 256 << 10;
+ static const int kAllocIncrement = 32 << 10;
// Aligned size of T
static const size_t kAlignedSize
= (((sizeof(T) + kAlignment - 1) / kAlignment) * kAlignment);
-
+
// Free area from which to carve new objects
char* free_area_;
size_t free_avail_;
@@ -330,7 +330,6 @@ class PageHeapAllocator {
free_area_ = NULL;
free_avail_ = 0;
free_list_ = NULL;
- New(); New(); // Reduces cache conflicts?
}
T* New() {
@@ -522,7 +521,7 @@ static Span sampled_objects;
// Map from page-id to per-page data
// -------------------------------------------------------------------------
-// We use PageMap1<> for 32-bit and PageMap3<> for 64-bit machines.
+// We use PageMap2<> for 32-bit and PageMap3<> for 64-bit machines.
// Selector class -- general selector uses 3-level map
template <int BITS> class MapSelector {
@@ -530,10 +529,10 @@ template <int BITS> class MapSelector {
typedef TCMalloc_PageMap3<BITS-kPageShift> Type;
};
-// A single-level map for 32-bit machines
+// A two-level map for 32-bit machines
template <> class MapSelector<32> {
public:
- typedef TCMalloc_PageMap1<32-kPageShift> Type;
+ typedef TCMalloc_PageMap2<32-kPageShift> Type;
};
// -------------------------------------------------------------------------
@@ -585,14 +584,7 @@ class TCMalloc_PageHeap {
// Return number of free bytes in heap
uint64_t FreeBytes() const {
- Length pages = 0;
- for (int length = 0; length < kMaxPages; length++) {
- pages += length * DLL_Length(&free_[length]);
- }
- for (Span* s = large_.next; s != &large_; s = s->next) {
- pages += s->length;
- }
- return (static_cast<uint64_t>(pages) << kPageShift);
+ return (static_cast<uint64_t>(free_pages_) << kPageShift);
}
bool Check();
@@ -609,6 +601,9 @@ class TCMalloc_PageHeap {
// Array mapping from span length to a doubly linked list of free spans
Span free_[kMaxPages];
+ // Number of pages kept in free lists
+ uintptr_t free_pages_;
+
// Bytes allocated from system
uint64_t system_bytes_;
@@ -630,6 +625,7 @@ class TCMalloc_PageHeap {
};
TCMalloc_PageHeap::TCMalloc_PageHeap() : pagemap_(MetaDataAlloc),
+ free_pages_(0),
system_bytes_(0) {
DLL_Init(&large_);
for (int i = 0; i < kMaxPages; i++) {
@@ -647,6 +643,7 @@ Span* TCMalloc_PageHeap::New(Length n) {
Span* result = free_[s].next;
Carve(result, n);
ASSERT(Check());
+ free_pages_ -= n;
return result;
}
}
@@ -665,6 +662,7 @@ Span* TCMalloc_PageHeap::New(Length n) {
if (best != NULL) {
Carve(best, n);
ASSERT(Check());
+ free_pages_ -= n;
return best;
}
if (i == 0) {
@@ -764,6 +762,7 @@ void TCMalloc_PageHeap::Delete(Span* span) {
} else {
DLL_InsertOrdered(&large_, span);
}
+ free_pages_ += n;
ASSERT(Check());
}
@@ -786,7 +785,8 @@ void TCMalloc_PageHeap::Dump(TCMalloc_Printer* out) {
if (!DLL_IsEmpty(&free_[s])) nonempty_sizes++;
}
out->printf("------------------------------------------------\n");
- out->printf("PageHeap: %d sizes\n", nonempty_sizes);
+ out->printf("PageHeap: %d sizes; %6.1f MB free\n", nonempty_sizes,
+ (static_cast<double>(free_pages_) * kPageSize) / 1048576.0);
out->printf("------------------------------------------------\n");
uint64_t cumulative = 0;
for (int s = 0; s < kMaxPages; s++) {
@@ -830,13 +830,15 @@ bool TCMalloc_PageHeap::GrowHeap(Length n) {
system_bytes_ += (ask << kPageShift);
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
ASSERT(p > 0);
-
+
// Make sure pagemap_ has entries for all of the new pages.
// Plus ensure one before and one after so coalescing code
// does not need bounds-checking.
if (pagemap_.Ensure(p-1, ask+2)) {
// Pretend the new area is allocated and then Delete() it to
// cause any necessary coalescing to occur.
+ //
+ // We do not adjust free_pages_ here since Delete() will do it for us.
Span* span = NewSpan(p, ask);
RecordSpan(span);
Delete(span);
@@ -1025,8 +1027,12 @@ static TCMalloc_Central_FreeListPadded central_cache[kNumClasses];
// Page-level allocator
static SpinLock pageheap_lock = SPINLOCK_INITIALIZER;
-static TCMalloc_PageHeap* pageheap = NULL;
static char pageheap_memory[sizeof(TCMalloc_PageHeap)];
+static bool phinited = false;
+
+// Avoid extra level of indirection by making "pageheap" be just an alias
+// of pageheap_memory.
+#define pageheap ((TCMalloc_PageHeap*) pageheap_memory)
// Thread-specific key. Initialization here is somewhat tricky
// because some Linux startup code invokes malloc() before it
@@ -1085,7 +1091,7 @@ void TCMalloc_Central_FreeList::Insert(void* object) {
ASSERT(p != object);
got++;
}
- ASSERT(got + span->refcount ==
+ ASSERT(got + span->refcount ==
(span->length<<kPageShift)/ByteSizeForClass(span->sizeclass));
}
@@ -1333,16 +1339,19 @@ void TCMalloc_ThreadCache::InitModule() {
// by doing one in the constructor of the module_enter_exit_hook
// object declared below.
SpinLockHolder h(&pageheap_lock);
- if (pageheap == NULL) {
+ if (!phinited) {
InitSizeClasses();
threadheap_allocator.Init();
span_allocator.Init();
+ span_allocator.New(); // Reduce cache conflicts
+ span_allocator.New(); // Reduce cache conflicts
stacktrace_allocator.Init();
DLL_Init(&sampled_objects);
for (int i = 0; i < kNumClasses; ++i) {
central_cache[i].Init(i);
}
- pageheap = new ((void*)pageheap_memory) TCMalloc_PageHeap;
+ new ((void*)pageheap_memory) TCMalloc_PageHeap;
+ phinited = 1;
}
}
@@ -1488,7 +1497,7 @@ static void ExtractStats(TCMallocStats* r, uint64_t* class_count) {
r->pageheap_bytes = pageheap->FreeBytes();
}
}
-
+
// WRITE stats to "out"
static void DumpStats(TCMalloc_Printer* out, int level) {
TCMallocStats stats;
@@ -1514,7 +1523,7 @@ static void DumpStats(TCMalloc_Printer* out, int level) {
SpinLockHolder h(&pageheap_lock);
pageheap->Dump(out);
}
-
+
const uint64_t bytes_in_use = stats.system_bytes
- stats.pageheap_bytes
- stats.central_bytes
@@ -1568,7 +1577,7 @@ static void** DumpStackTraces() {
needed_slots);
return NULL;
}
-
+
SpinLockHolder h(&pageheap_lock);
int used_slots = 0;
for (Span* s = sampled_objects.next; s != &sampled_objects; s = s->next) {
@@ -1597,7 +1606,13 @@ class TCMallocImplementation : public MallocExtension {
virtual void GetStats(char* buffer, int buffer_length) {
ASSERT(buffer_length > 0);
TCMalloc_Printer printer(buffer, buffer_length);
- DumpStats(&printer, 2);
+
+ // Print level one stats unless lots of space is available
+ if (buffer_length < 10000) {
+ DumpStats(&printer, 1);
+ } else {
+ DumpStats(&printer, 2);
+ }
}
virtual void** ReadStackTraces() {
@@ -1627,9 +1642,8 @@ class TCMallocImplementation : public MallocExtension {
if (strcmp(name, "tcmalloc.slack_bytes") == 0) {
// We assume that bytes in the page heap are not fragmented too
// badly, and are therefore available for allocation.
- TCMallocStats stats;
- ExtractStats(&stats, NULL);
- *value = stats.pageheap_bytes;
+ SpinLockHolder l(&pageheap_lock);
+ *value = pageheap->FreeBytes();
return true;
}
@@ -1667,6 +1681,14 @@ class TCMallocImplementation : public MallocExtension {
}
};
+// RedHat 9's pthread manager allocates an object directly by calling
+// a __libc_XXX() routine. This memory block is not known to tcmalloc.
+// At cleanup time, the pthread manager calls free() on this
+// pointer, which then crashes.
+//
+// We hack around this problem by disabling all deallocations
+// after a global object destructor in this module has been called.
+static bool tcmalloc_is_destroyed = false;
//-------------------------------------------------------------------
// Helpers for the exported routines below
@@ -1694,7 +1716,7 @@ static Span* DoSampledAllocation(size_t size) {
span->sample = 1;
span->objects = stack;
DLL_Prepend(&sampled_objects, span);
-
+
return span;
}
@@ -1722,10 +1744,22 @@ static inline void* do_malloc(size_t size) {
static inline void do_free(void* ptr) {
if (TCMallocDebug::level >= TCMallocDebug::kVerbose)
MESSAGE("In tcmalloc do_free(%p)\n", ptr);
- if (ptr == NULL) return;
+ if (ptr == NULL || tcmalloc_is_destroyed) return;
ASSERT(pageheap != NULL); // Should not call free() before malloc()
const PageID p = reinterpret_cast<uintptr_t>(ptr) >> kPageShift;
Span* span = pageheap->GetDescriptor(p);
+
+ if (span == NULL) {
+ // We've seen systems where a piece of memory allocated using the
+ // allocator built in to libc is deallocated using free() and
+ // therefore ends up inside tcmalloc which can't find the
+ // corresponding span. We silently throw this object on the floor
+ // instead of crashing.
+ MESSAGE("tcmalloc: ignoring potential glibc-2.3.5 induced free "
+ "of an unknown object %p\n", ptr);
+ return;
+ }
+
ASSERT(span != NULL);
ASSERT(!span->free);
const size_t cl = span->sizeclass;
@@ -2023,6 +2057,31 @@ extern "C" void malloc_stats(void) {
PrintStats(1);
}
+extern "C" int mallopt(int cmd, int value) {
+ return 1; // Indicates error
+}
+
+extern "C" struct mallinfo mallinfo(void) {
+ TCMallocStats stats;
+ ExtractStats(&stats, NULL);
+
+ // Just some of the fields are filled in.
+ struct mallinfo info;
+ memset(&info, 0, sizeof(info));
+
+ // Unfortunately, the struct contains "int" field, so some of the
+ // size values will be truncated.
+ info.arena = static_cast<int>(stats.system_bytes);
+ info.fsmblks = static_cast<int>(stats.thread_bytes + stats.central_bytes);
+ info.fordblks = static_cast<int>(stats.pageheap_bytes);
+ info.uordblks = static_cast<int>(stats.system_bytes
+ - stats.thread_bytes
+ - stats.central_bytes
+ - stats.pageheap_bytes);
+
+ return info;
+}
+
//-------------------------------------------------------------------
// Some library routines on RedHat 9 allocate memory using malloc()
// and free it using __libc_free() (or vice-versa). Since we provide
@@ -2042,7 +2101,7 @@ extern "C" {
void* __libc_memalign(size_t align, size_t s) ALIAS("memalign");
void* __libc_valloc(size_t size) ALIAS("valloc");
void* __libc_pvalloc(size_t size) ALIAS("pvalloc");
- void* __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign");
+ int __posix_memalign(void** r, size_t a, size_t s) ALIAS("posix_memalign");
#undef ALIAS
#else
// Portable wrappers
@@ -2054,7 +2113,7 @@ extern "C" {
void* __libc_memalign(size_t align, size_t s) { return memalign(align, s); }
void* __libc_valloc(size_t size) { return valloc(size); }
void* __libc_pvalloc(size_t size) { return pvalloc(size); }
- void* __posix_memalign(void** r, size_t a, size_t s) {
+ int __posix_memalign(void** r, size_t a, size_t s) {
return posix_memalign(r, a, s);
}
#endif
diff --git a/src/tests/heap-checker_unittest.cc b/src/tests/heap-checker_unittest.cc
index b13d617..d56f760 100644
--- a/src/tests/heap-checker_unittest.cc
+++ b/src/tests/heap-checker_unittest.cc
@@ -78,6 +78,7 @@
#include <set>
#include <string>
+#include <errno.h> // errno
#include <netinet/in.h> // inet_ntoa
#include <arpa/inet.h> // inet_ntoa
#ifdef HAVE_EXECINFO_H
diff --git a/src/tests/stacktrace_unittest.cc b/src/tests/stacktrace_unittest.cc
index afca1ff..34a7718 100644
--- a/src/tests/stacktrace_unittest.cc
+++ b/src/tests/stacktrace_unittest.cc
@@ -38,11 +38,60 @@
#include <execinfo.h>
#endif
+// Obtain a backtrace, verify that the expected callers are present in the
+// backtrace, and maybe print the backtrace to stdout.
+
+//-----------------------------------------------------------------------//
+void CheckStackTrace4(int i);
+void CheckStackTrace3(int i);
+void CheckStackTrace2(int i);
+void CheckStackTrace1(int i);
void CheckStackTrace(int i);
+//-----------------------------------------------------------------------//
+
+// The sequence of functions whose return addresses we expect to see in the
+// backtrace.
+const int BACKTRACE_STEPS = 5;
+void * expected_stack[BACKTRACE_STEPS] = {
+ (void *) &CheckStackTrace4,
+ (void *) &CheckStackTrace3,
+ (void *) &CheckStackTrace2,
+ (void *) &CheckStackTrace1,
+ (void *) &CheckStackTrace,
+};
+
+// Depending on the architecture/compiler/libraries, (not sure which)
+// the current function may or may not appear in the backtrace.
+// For gcc-2:
+//
+// stack[0] is ret addr within CheckStackTrace4
+// stack[1] is ret addr within CheckStackTrace3
+// stack[2] is ret addr within CheckStackTrace2
+// stack[3] is ret addr within CheckStackTrace1
+// stack[4] is ret addr within CheckStackTrace
+//
+// For gcc3-k8:
+//
+// stack[0] is ret addr within CheckStackTraceLeaf
+// stack[1] is ret addr within CheckStackTrace4
+// ...
+// stack[5] is ret addr within CheckStackTrace
+
+// The google version does not include the caller in the
+// backtrace. Some other version might. (glibc backtrace()?)
+const int self_in_backtrace = 0;
+
+//-----------------------------------------------------------------------//
+
+void CheckRetAddrIsInFunction( void * ret_addr, void * function_start_addr)
+{
+ const int typ_fn_len = 0x40; // assume relevant functions are only 0x40 bytes long
+ CHECK_GE(ret_addr, function_start_addr);
+ CHECK_LE(ret_addr, (void *) ((char *) function_start_addr + typ_fn_len));
+}
+
+//-----------------------------------------------------------------------//
-/* Obtain a backtrace, verify that we are the great-great-grandchild of
- * CheckStackTrace, and maybe print the backtrace to stdout.
- */
void CheckStackTraceLeaf(void) {
const int STACK_LEN = 10;
void *stack[STACK_LEN];
@@ -52,14 +101,10 @@ void CheckStackTraceLeaf(void) {
printf("Obtained %d stack frames.\n", size);
CHECK_LE(size, STACK_LEN);
- // for some reason, CheckStackTraceLeaf doesn't show up in the backtrace
- // stack[size - 1] is in CheckStackTrace4
- // stack[size - 2] is in CheckStackTrace3
- // stack[size - 3] is in CheckStackTrace2
- // stack[size - 4] is in CheckStackTrace1
- // stack[size - 5] is in CheckStackTrace
- CHECK_GE(stack[size - 4], (void*) &CheckStackTrace);
- CHECK_LE(stack[size - 4], (char*) &CheckStackTrace + 0x40); // assume function is only 0x40 bytes long
+ for (int i = 0; i < BACKTRACE_STEPS; i++)
+ {
+ CheckRetAddrIsInFunction(stack[i + self_in_backtrace], expected_stack[i]);
+ }
#ifdef HAVE_EXECINFO_H
@@ -75,6 +120,8 @@ void CheckStackTraceLeaf(void) {
}
+//-----------------------------------------------------------------------//
+
/* Dummy functions to make the backtrace more interesting. */
void CheckStackTrace4(int i) { for (int j = i; j >= 0; j--) CheckStackTraceLeaf(); }
void CheckStackTrace3(int i) { for (int j = i; j >= 0; j--) CheckStackTrace4(j); }
@@ -82,6 +129,8 @@ void CheckStackTrace2(int i) { for (int j = i; j >= 0; j--) CheckStackTrace3(j);
void CheckStackTrace1(int i) { for (int j = i; j >= 0; j--) CheckStackTrace2(j); }
void CheckStackTrace(int i) { for (int j = i; j >= 0; j--) CheckStackTrace1(j); }
+//-----------------------------------------------------------------------//
+
int main(int argc, char ** argv) {
CheckStackTrace(0);