diff options
author | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2007-03-22 04:42:30 +0000 |
---|---|---|
committer | csilvers <csilvers@6b5cf1ce-ec42-a296-1ba9-69fdba395a50> | 2007-03-22 04:42:30 +0000 |
commit | bc455d7b63949fab94ed9518d277866e95f08768 (patch) | |
tree | 50e71170b8d89cece878f7f337cc9abcbc8d4eed | |
parent | 91fad389784766782263133c5510976a8f76d89e (diff) | |
download | gperftools-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-- | ChangeLog | 8 | ||||
-rwxr-xr-x | configure | 20 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/pagemap.h | 60 | ||||
-rw-r--r-- | src/stacktrace.cc | 7 | ||||
-rw-r--r-- | src/tcmalloc.cc | 121 | ||||
-rw-r--r-- | src/tests/heap-checker_unittest.cc | 1 | ||||
-rw-r--r-- | src/tests/stacktrace_unittest.cc | 71 |
8 files changed, 235 insertions, 55 deletions
@@ -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) @@ -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); |