summaryrefslogtreecommitdiffstats
path: root/kmtrace
diff options
context:
space:
mode:
Diffstat (limited to 'kmtrace')
-rw-r--r--kmtrace/Makefile.am50
-rw-r--r--kmtrace/README110
-rw-r--r--kmtrace/configure.in.in18
-rw-r--r--kmtrace/demangle.cpp60
-rw-r--r--kmtrace/kde.excludes49
-rwxr-xr-xkmtrace/kminspector.in9
-rw-r--r--kmtrace/kmtrace.cpp721
-rw-r--r--kmtrace/ksotrace.cpp11
-rw-r--r--kmtrace/ktrace.c840
-rw-r--r--kmtrace/ktrace.h10
-rw-r--r--kmtrace/match.cpp73
-rw-r--r--kmtrace/mtrace.c383
12 files changed, 2334 insertions, 0 deletions
diff --git a/kmtrace/Makefile.am b/kmtrace/Makefile.am
new file mode 100644
index 00000000..68fce88d
--- /dev/null
+++ b/kmtrace/Makefile.am
@@ -0,0 +1,50 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1996-1997 Matthias Kalle Dalheimer (kalle@kde.org)
+# (C) 1997-1998 Stephan Kulow (coolo@kde.org)
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public License
+# along with this library; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+AM_CPPFLAGS = -DQT_NO_ASCII_CAST -UQT_NO_COMPAT -O3
+LDADD = $(LIB_KDECORE) -liberty
+INCLUDES = $(all_includes)
+
+bin_PROGRAMS = kmtrace demangle kmmatch
+kmtrace_SOURCES = kmtrace.cpp
+kmtrace_LDFLAGS = $(all_libraries)
+
+demangle_SOURCES = demangle.cpp
+demangle_LDFLAGS = $(all_libraries)
+
+kmmatch_SOURCES = match.cpp
+kmmatch_LDFLAGS = $(all_libraries)
+
+bin_SCRIPTS = kminspector
+
+lib_LIBRARIES = libktrace_s.a
+libktrace_s_a_SOURCES = ktrace_s.c
+
+ktrace_s.c: $(srcdir)/ktrace.c
+ $(LN_S) $(srcdir)/ktrace.c $@
+
+lib_LTLIBRARIES = libktrace.la
+libktrace_la_LDFLAGS = -avoid-version
+libktrace_la_SOURCES = ksotrace.cpp ktrace.c
+libktrace_la_LIBADD = $(LIBDL)
+
+exclude_DATA = kde.excludes
+excludedir = $(kde_datadir)/kmtrace
+
+include_HEADERS = ktrace.h
diff --git a/kmtrace/README b/kmtrace/README
new file mode 100644
index 00000000..63e35228
--- /dev/null
+++ b/kmtrace/README
@@ -0,0 +1,110 @@
+This is a KDE tool to assist with malloc debugging using glibc's "mtrace"
+functionality. Unfortunately the mtrace that is part of current (9/9/2000)
+glibc versions only logs the return-address of the malloc/free call.
+The file mtrace.c in this directory logs a complete backtrace upon malloc/
+free.
+
+THIS PROGRAM DEPENDS ON GLIBC! It does not pretend to be portable.
+
+Howto use:
+
+Install the libktrace.so shared library, the ktrace.h header file, the
+and kde.excludes file and the kmtrace processing tool with:
+
+ make install
+
+There are two ways to activate memory usage loggings by ktrace :
+
+1) The LD_PRELOAD way
+
+This way, you can debug any application without having to recompile it,
+but you'll have to debug also the memory allocated by KApplication and
+friends.
+
+You can activate malloc logging by starting yourApplication as:
+
+ MALLOC_TRACE=/tmp/ktrace.out LD_PRELOAD=$KDEDIR/lib/libktrace.so yourApplication
+
+2) The manual way
+
+Take the KDE application that you want to investigate and add
+
+ #include <ktrace.h>
+
+Add as first statement in main():
+
+ ktrace();
+
+Add ktrace_s.a to the LDADD line in your Makefile.am like:
+
+ kicker_LDADD = kicker.la /opt/kde/lib/libktrace_s.a
+
+Note that the static library is used.
+You can now activate malloc logging by starting yourApplication as:
+
+ MALLOC_TRACE=/tmp/ktrace.out yourApplication
+
+This will generate a huge log in /tmp/ktrace.out.
+
+You can process this log with:
+
+ kmtrace /tmp/ktrace.out > ktrace.parsed
+
+By default the trace-output is stored in the current directory
+as "ktrace.out". kmtrace also searches it there, so you don't need
+to add any commandline options.
+
+TIPS
+====
+
+* If you can't be bothered with the stuff that KApplication allocates for you
+you might want to put the ktrace() call after the KApplication constructor.
+This will lead to a lot of warnings like:
+
+ Freeing unalloacted memory: 0x08056108
+
+Which are harmless, it just means that the memory was allocated before you
+turned the tracing on. Note that you cannot use this if you're using
+LD_PRELOAD to use ktrace.
+
+* To filter out harmless allocations out of the output file you can specify
+with the --exclude option a file with symbols to exclude from output. If a
+backtrace contains a symbol that starts with any of the symbols in this file,
+this backtrace / leaked block is not shown in the output.
+
+In the file kde.exclude some example symbols are listed. Usage example:
+
+ kmtrace /tmp/malloc.trace > /tmp/malloc.parsed
+
+* Be aware that the reported symbols may not be accurate under all
+circumstances. E.g. consider the following backtrace:
+
+ 0x405879c1 /lib/libdl.so.2(dlerror+0x1b1)
+ 0x405873b3 /lib/libdl.so.2(dlopen+0x33)
+ 0x4053c0b2 /ext/kde2.0/lib/libkdecore.so.3(QXmlSimpleReader::reportParseErro
+ 0x4053c74b /ext/kde2.0/lib/libkdecore.so.3(lt_dlexit+0x24b)
+ 0x4053c894 /ext/kde2.0/lib/libkdecore.so.3(lt_dlexit+0x394)
+ 0x4053dd49 /ext/kde2.0/lib/libkdecore.so.3(lt_dlopen+0x899)
+
+The QXmlSimpleReader is obviously wrong here.
+
+* You can use the --tree option to kmtrace to specify a file to print a tree
+of the allocations. You can also use --treedepth and --treethreshold options
+to hide subtrees that are deeper than the specified depth or allocated less
+than the given memory amount.
+
+* The advantage of using libktrace_s.a (the static library) is that you can
+put calls to ktrace() and kuntrace() around a block of code that
+interests you. Only allocations and deallocations between the first call
+to ktrace() and the first call to kuntrace() will be logged.
+
+
+Have fun.
+
+Waldo Bastian
+bastian@kde.org
+
+kmtrace.cpp by Waldo Bastian <bastian@kde.org>
+ktrace.c by Waldo Bastian <bastian@kde.org> based on mtrace.c
+mtrace.c by Mike Haertel <mike@ai.mit.edu>
+mtrace.c patched by Andi Kleen <ak@suse.de>
diff --git a/kmtrace/configure.in.in b/kmtrace/configure.in.in
new file mode 100644
index 00000000..5df3711e
--- /dev/null
+++ b/kmtrace/configure.in.in
@@ -0,0 +1,18 @@
+dnl AC_OUTPUT( kmtrace/kminspector )
+
+case "$host" in
+ *-gnu)
+ saved_LIBS="$LIBS"
+ LIBS="$LIBS -Wl,-Bstatic -liberty -Wl,-Bdynamic"
+ AC_TRY_LINK([], [], [kde_compile_kmtrace=$GCC], [kde_compile_kmtrace=no])
+ AC_SUBST(KMTRACE_LIBS, [$LIBS])
+ LIBS="$saved_LIBS"
+ ;;
+ *)
+ kde_compile_kmtrace=no
+ ;;
+esac
+
+if test ! "x$kde_compile_kmtrace" = "xyes"; then
+ DO_NOT_COMPILE="$DO_NOT_COMPILE kmtrace"
+fi
diff --git a/kmtrace/demangle.cpp b/kmtrace/demangle.cpp
new file mode 100644
index 00000000..81bcff90
--- /dev/null
+++ b/kmtrace/demangle.cpp
@@ -0,0 +1,60 @@
+#include <qintdict.h>
+#include <stdio.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qtextstream.h>
+#include <qsortedlist.h>
+#include <qfile.h>
+#include <qtl.h>
+#include <qvaluelist.h>
+#include <stdlib.h>
+#include <ktempfile.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kcmdlineargs.h>
+
+extern "C" {
+/* Options passed to cplus_demangle (in 2nd parameter). */
+
+#define DMGL_NO_OPTS 0 /* For readability... */
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */
+
+#define DMGL_AUTO (1 << 8)
+#define DMGL_GNU (1 << 9)
+#define DMGL_LUCID (1 << 10)
+#define DMGL_ARM (1 << 11)
+#define DMGL_HP (1 << 12) /* For the HP aCC compiler; same as ARM
+ except for template arguments, etc. */
+#define DMGL_EDG (1 << 13)
+#define DMGL_GNU_V3 (1 << 14)
+#define DMGL_GNAT (1 << 15)
+
+
+extern char *cplus_demangle(const char *mangled, int options);
+}
+
+
+int main(int argc, char **argv)
+{
+ char buf[1024];
+
+ while(!feof(stdin))
+ {
+ fgets(buf, 1024, stdin);
+ QCString line = buf;
+ line = line.stripWhiteSpace();
+ char *res = cplus_demangle(line.data(), DMGL_PARAMS | DMGL_AUTO | DMGL_ANSI );
+ if (res)
+ {
+ printf("%s\n", res);
+ free(res);
+ }
+ else
+ {
+ printf("%s\n", line.data());
+ }
+ }
+}
+
diff --git a/kmtrace/kde.excludes b/kmtrace/kde.excludes
new file mode 100644
index 00000000..9e77eb5a
--- /dev/null
+++ b/kmtrace/kde.excludes
@@ -0,0 +1,49 @@
+# We don't care about initialisation done by X11.
+XLoadQueryFont
+_XInitKeysymDB
+IceRegisterForProtocolSetup
+KDE_IceRegisterForProtocolSetup
+IceOpenConnection
+KDE_IceOpenConnection
+KDE_IceProtocolSetup
+#and we don't care about Xim memleaks
+qt_init_internal
+QApplication::create_xim
+# workaround for Qt 2.2.0 bug
+QFontDatabase::QFontDatabase
+QFontDatabase::charSets
+
+#QRexExp does caching(?)
+QRegExpEngine::QRegExpEngine
+QRegExpEngine::parseExpression
+
+QStyleSheet::defaultSheet
+
+QTimer::singleShot
+QTextCodec::codecForLocale
+# Yeah, yeah we know that this metaobject stuff is never free'ed.
+QMetaObject::new_meta
+QObject::initMetaObject
+QObject::staticMetaObject
+QString::sprintf
+QWidget::createTLExtra
+# static KDE stuff
+kdbgstream::flush
+KCmdLineArgs::addCmdLineOptions
+k_bindtextdomain
+_nl_find_domain
+KIconTheme::list
+KIconTheme::current
+KGlobalSettings::toolBarFont
+KGlobalSettings::menuFont
+KGlobalSettings::fixedFont
+KGlobalSettings::generalFont
+KGlobalSettings::toolBarFont
+KImageIOFactory::self
+objMap
+# Some C functions that allocate data for initialisation
+getpwuid
+adjtime
+getservbyname
+# nothing created by static objects can be a memory leak
+__static_initialization_and_destruction_0
diff --git a/kmtrace/kminspector.in b/kmtrace/kminspector.in
new file mode 100755
index 00000000..572ed200
--- /dev/null
+++ b/kmtrace/kminspector.in
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+export MALLOC_TREE=kminspector.tree
+export MALLOC_THRESHOLD=2000
+export LD_PRELOAD=@kde_libraries@/libktrace.so
+
+$*
+
+cat kminspector.tree | less
diff --git a/kmtrace/kmtrace.cpp b/kmtrace/kmtrace.cpp
new file mode 100644
index 00000000..82055e43
--- /dev/null
+++ b/kmtrace/kmtrace.cpp
@@ -0,0 +1,721 @@
+#include <qintdict.h>
+#include <stdio.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qtextstream.h>
+#include <qsortedlist.h>
+#include <qfile.h>
+#include <qtl.h>
+#include <qvaluelist.h>
+#include <stdlib.h>
+#include <ktempfile.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kcmdlineargs.h>
+#include <kprocess.h>
+
+extern "C" {
+/* Options passed to cplus_demangle (in 2nd parameter). */
+
+#define DMGL_NO_OPTS 0 /* For readability... */
+#define DMGL_PARAMS (1 << 0) /* Include function args */
+#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */
+#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */
+
+#define DMGL_AUTO (1 << 8)
+#define DMGL_GNU (1 << 9)
+#define DMGL_LUCID (1 << 10)
+#define DMGL_ARM (1 << 11)
+#define DMGL_HP (1 << 12) /* For the HP aCC compiler; same as ARM
+ except for template arguments, etc. */
+#define DMGL_EDG (1 << 13)
+#define DMGL_GNU_V3 (1 << 14)
+#define DMGL_GNAT (1 << 15)
+
+
+extern char *cplus_demangle(const char *mangled, int options);
+}
+
+struct Entry {
+ int base;
+ int size;
+ int signature;
+ int count;
+ int total_size;
+ int backtrace[1];
+
+ bool operator==(const Entry &e) { return total_size == e.total_size; }
+ bool operator<(const Entry &e) { return total_size > e.total_size; }
+};
+
+QIntDict<Entry> *entryDict = 0;
+QIntDict<char> *symbolDict = 0;
+QIntDict<char> *formatDict = 0;
+QSortedList<Entry> *entryList = 0;
+QStrList *excludes = 0;
+
+const char * const unknown = "<unknown>";
+const char * const excluded = "<excluded>";
+int allocCount = 0;
+int leakedCount = 0;
+int count = 0;
+int maxCount;
+int totalBytesAlloced = 0;
+int totalBytesLeaked = 0;
+int totalBytes = 0;
+int maxBytes;
+
+int fromHex(const char *str);
+void parseLine(const QCString &_line, char operation);
+void dumpBlocks();
+
+int fromHex(const char *str)
+{
+ if (*str == '[') str++;
+ str += 2; // SKip "0x"
+ return strtoll(str, NULL, 16);
+}
+
+// [address0][address1] .... [address] + base size
+void parseLine(const QCString &_line, char operation)
+{
+ char *line= (char *) _line.data();
+ const char *cols[200];
+ int i = 0;
+ cols[i++] = line;
+ while(*line)
+ {
+ if (*line == ' ')
+ {
+ *line = 0;
+ line++;
+ while (*line && (*line==' ')) line++;
+ if (*line) cols[i++] = line;
+ }
+ else line++;
+ }
+ int cols_count = i;
+ if (cols_count > 199) fprintf(stderr, "Error cols_count = %d\n", cols_count);
+ if (cols_count < 4) return;
+ switch (operation)
+ {
+ case '+':
+ {
+ Entry *entry = (Entry *) malloc((cols_count+3) *sizeof(int));
+ entry->base = fromHex(cols[cols_count-2]);
+ entry->size = fromHex(cols[cols_count-1]);
+ int signature = 0;
+ for(int i = cols_count-3; i--;)
+ {
+ signature += (entry->backtrace[i-1] = fromHex(cols[i]));
+ }
+ entry->signature = (signature / 4)+cols_count;
+ entry->count = 1;
+ entry->total_size = entry->size;
+ entry->backtrace[cols_count-4] = 0;
+ totalBytesAlloced += entry->size;
+ totalBytes += entry->size;
+ count++;
+ if (totalBytes > maxBytes)
+ maxBytes = totalBytes;
+ if (count > maxCount)
+ maxCount = count;
+ if (entryDict->find(entry->base))
+ fprintf(stderr, "\rAllocated twice: 0x%08x \n", entry->base);
+ entryDict->replace(entry->base, entry);
+ } break;
+ case '-':
+ {
+ int base = fromHex(cols[cols_count-1]);
+ Entry *entry = entryDict->take(base);
+ if (!entry)
+ {
+ if (base)
+ fprintf(stderr, "\rFreeing unalloacted memory: 0x%08x \n", base);
+ }
+ else
+ {
+ totalBytes -= entry->size;
+ count--;
+ free(entry);
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+void sortBlocks()
+{
+ QIntDictIterator<Entry> it(*entryDict);
+ for(;it.current(); ++it)
+ {
+ Entry *entry = it.current();
+ totalBytesLeaked += entry->total_size;
+ entryList->append(entry);
+ for(int i = 0; entry->backtrace[i]; i++)
+ {
+ if (!symbolDict->find(entry->backtrace[i]))
+ symbolDict->insert(entry->backtrace[i], unknown);
+ }
+ }
+ entryList->sort();
+}
+
+void collectDupes()
+{
+ QIntDict<Entry> dupeDict;
+ QIntDictIterator<Entry> it(*entryDict);
+ for(;it.current();)
+ {
+ Entry *entry = it.current();
+ ++it;
+ Entry *entry2 = dupeDict.find(entry->signature);
+ if (entry2)
+ {
+ entry2->count++;
+ entry2->total_size += entry->size;
+ entryDict->remove(entry->base);
+ }
+ else
+ {
+ dupeDict.insert(entry->signature, entry);
+ }
+ }
+}
+
+int lookupSymbols(FILE *stream)
+{
+ int i = 0;
+ int symbols = 0;
+ char line2[1024];
+ while(!feof(stream))
+ {
+ fgets(line2, 1023, stream);
+ if (line2[0] == '=' )
+ {
+ if(strcmp(line2,"= End") == 0 )
+ break;
+ }
+ else if (line2[0] == '#')
+ ;
+ else if (line2[0] == '@')
+ ;
+ else if (line2[0] == '[')
+ ;
+ else if (line2[0] == '-')
+ ;
+ else if (line2[0] == '<')
+ ;
+ else if (line2[0] == '>')
+ ;
+ else if (line2[0] == '+')
+ {
+ i++;
+ if (i & 1024)
+ {
+ fprintf(stderr, "\rLooking up symbols: %d found %d of %d symbols", i, symbols, symbolDict->count());
+ }
+ }
+ else
+ {
+ char *addr = index(line2, '[');
+ if (addr)
+ {
+ long i_addr = fromHex(addr);
+ const char* str = symbolDict->find(i_addr);
+ if (str == unknown)
+ {
+ *addr = 0;
+ char* str;
+ if( rindex(line2, '/') != NULL )
+ str = qstrdup(rindex(line2, '/')+1);
+ else
+ str = qstrdup(line2);
+ symbolDict->replace(i_addr, str);
+ symbols++;
+ }
+ }
+ }
+ }
+ fprintf(stderr, "\rLooking up symbols: %d found %d of %d symbols\n", i, symbols, symbolDict->count());
+ return symbolDict->count()-symbols;
+}
+
+void lookupUnknownSymbols(const char *appname)
+{
+ KTempFile inputFile;
+ KTempFile outputFile;
+ inputFile.setAutoDelete(true);
+ outputFile.setAutoDelete(true);
+ FILE *fInputFile = inputFile.fstream();
+ QIntDict<char> oldDict = *symbolDict;
+ QIntDictIterator<char> it(oldDict);
+ for(;it.current(); ++it)
+ {
+ fprintf(fInputFile, "%08lx\n", it.currentKey());
+ }
+ inputFile.close();
+ QCString command;
+ command.sprintf("addr2line -e %s -f -C -s < %s > %s", appname,
+ QFile::encodeName(KProcess::quote(inputFile.name())).data(),
+ QFile::encodeName(KProcess::quote(outputFile.name())).data());
+ system(command.data());
+ fInputFile = fopen(QFile::encodeName(outputFile.name()), "r");
+ if (!fInputFile)
+ {
+ fprintf(stderr, "Error opening temp file.\n");
+ return;
+ }
+ QIntDictIterator<char> it2(oldDict);
+ char buffer1[1024];
+ char buffer2[1024];
+ for(;it2.current(); ++it2)
+ {
+ if (feof(fInputFile))
+ {
+ fprintf(stderr, "Premature end of symbol output.\n");
+ fclose(fInputFile);
+ return;
+ }
+ if (!fgets(buffer1, 1023, fInputFile)) continue;
+ if (!fgets(buffer2, 1023, fInputFile)) continue;
+ buffer1[strlen(buffer1)-1]=0;
+ buffer2[strlen(buffer2)-1]=0;
+ QCString symbol;
+ symbol.sprintf("%s(%s)", buffer2, buffer1);
+ if(*buffer1 != '?')
+ symbolDict->replace(it2.currentKey(),qstrdup(symbol.data()));
+ }
+ fclose(fInputFile);
+}
+
+int match(const char *s1, const char *s2)
+{
+ register int result;
+ while(true)
+ {
+ result = *s1 - *s2;
+ if (result)
+ return result;
+ s1++;
+ s2++;
+ if (!*s2) return 0;
+ if (!*s1) return -1;
+ }
+ return 0;
+}
+
+const char *lookupAddress(int addr)
+{
+ char *str = formatDict->find(addr);
+ if (str) return str;
+ QCString s = symbolDict->find(addr);
+ if (s.isEmpty())
+ {
+fprintf(stderr, "Error!\n");
+ exit(1);
+ }
+ else
+ {
+ int start = s.find('(');
+ int end = s.findRev('+');
+ if (end < 0)
+ end = s.findRev(')');
+ if ((start > 0) && (end > start))
+ {
+ QCString symbol = s.mid(start+1, end-start-1);
+ char *res = 0;
+ if (symbol.find(')') == -1)
+ res = cplus_demangle(symbol.data(), DMGL_PARAMS | DMGL_AUTO | DMGL_ANSI );
+
+ if (res)
+ {
+ symbol = res;
+ free(res);
+ }
+ res = (char *) symbol.data();
+ for(const char *it = excludes->first();it;it = excludes->next())
+ {
+ int i = match(res, it);
+ if (i == 0)
+ {
+ formatDict->insert(addr,excluded);
+ return excluded;
+ }
+ }
+ s.replace(start+1, end-start-1, symbol);
+ }
+ }
+ str = qstrdup(s.data());
+ formatDict->insert(addr,str);
+ return str;
+}
+
+void dumpBlocks()
+{
+ int filterBytes = 0;
+ int filterCount = 0;
+ for(Entry *entry = entryList->first();entry; entry = entryList->next())
+ {
+ for(int i = 0; entry->backtrace[i]; i++)
+ {
+ const char *str = lookupAddress(entry->backtrace[i]);
+ if (str == excluded)
+ {
+ entry->total_size = 0;
+ continue;
+ }
+ }
+ if (!entry->total_size) continue;
+ filterBytes += entry->total_size;
+ filterCount++;
+ }
+ printf("Leaked memory after filtering: %d bytes in %d blocks.\n", filterBytes, filterCount);
+ for(Entry *entry = entryList->first();entry; entry = entryList->next())
+ {
+ if (!entry->total_size) continue;
+ printf("[%d bytes in %d blocks, 1st. block is %d bytes at 0x%08x] ", entry->total_size, entry->count, entry->size, entry->base);
+ printf("\n");
+ for(int i = 0; entry->backtrace[i]; i++)
+ {
+ const char *str = lookupAddress(entry->backtrace[i]);
+ printf(" 0x%08x %s\n", entry->backtrace[i], str);
+ }
+ }
+}
+
+struct TreeEntry
+{
+ int address; // backtrace
+ int total_size;
+ int total_count;
+ typedef QValueList < TreeEntry > TreeList;
+ TreeList *subentries () const;
+ mutable TreeList *_subentries;
+ TreeEntry (int adr = 0, int size = 0, int count = 0, TreeList * sub = NULL );
+ bool operator == (const TreeEntry &) const;
+ bool operator < (const TreeEntry &) const;
+};
+
+typedef QValueList < TreeEntry > TreeList;
+
+inline TreeEntry::TreeEntry (int adr, int size, int count, TreeList * sub)
+ : address (adr), total_size (size), total_count (count), _subentries (sub)
+{
+}
+
+inline bool TreeEntry::operator == (const TreeEntry & r) const
+{ // this one is for QValueList
+ return address == r.address;
+}
+
+inline
+bool TreeEntry::operator < (const TreeEntry & r) const
+{ // this one is for qBubbleSort() ... yes, ugly hack
+ // the result is also reversed to get descending order
+ return total_size > r.total_size;
+}
+
+inline TreeList * TreeEntry::subentries () const
+{ // must be allocated only on-demand
+ if (_subentries == NULL)
+ _subentries = new TreeList; // this leaks memory, but oh well
+ return _subentries;
+}
+
+TreeList * treeList = 0;
+
+void buildTree ()
+{
+ for (Entry * entry = entryList->first ();
+ entry != NULL; entry = entryList->next ())
+ {
+ if (!entry->total_size)
+ continue;
+ TreeList * list = treeList;
+ int i;
+ for (i = 0; entry->backtrace[i]; ++i)
+ ; // find last (topmost) backtrace entry
+ for (--i; i >= 0; --i)
+ {
+ TreeList::Iterator pos = list->find (entry->backtrace[i]);
+ if (pos == list->end ())
+ {
+ list->prepend (TreeEntry (entry->backtrace[i], entry->total_size,
+ entry->count));
+ pos = list->find (entry->backtrace[i]);
+ }
+ else
+ *pos = TreeEntry (entry->backtrace[i],
+ entry->total_size + (*pos).total_size,
+ entry->count + (*pos).total_count,
+ (*pos)._subentries);
+ list = (*pos).subentries ();
+ }
+ }
+}
+
+void processTree (TreeList * list, int threshold, int maxdepth, int depth)
+{
+ if (++depth > maxdepth && maxdepth > 0) // maxdepth <= 0 means no limit
+ return;
+ for (TreeList::Iterator it = list->begin (); it != list->end ();)
+ {
+ if ((*it).subentries ()->count () > 0)
+ processTree ((*it).subentries (), threshold, maxdepth, depth);
+ if ((*it).total_size < threshold || (depth > maxdepth && maxdepth > 0))
+ {
+ it = list->remove (it);
+ continue;
+ }
+ ++it;
+ }
+ qBubbleSort (*list);
+}
+
+void
+dumpTree (const TreeEntry & entry, int level, char *indent, FILE * file)
+{
+ bool extra_ind = (entry.subentries ()->count () > 0);
+ if(extra_ind)
+ indent[level++] = '+';
+ indent[level] = '\0';
+ char savindent[2];
+ const char * str = lookupAddress (entry.address);
+ fprintf (file, "%s- %d/%d %s[0x%08x]\n", indent,
+ entry.total_size, entry.total_count, str, entry.address);
+ if (level > 1)
+ {
+ savindent[0] = indent[level - 2];
+ savindent[1] = indent[level - 1];
+ if (indent[level - 2] == '+')
+ indent[level - 2] = '|';
+ else if (indent[level - 2] == '\\')
+ indent[level - 2] = ' ';
+ }
+ int pos = 0;
+ int last = entry.subentries ()->count() - 1;
+ for (TreeList::ConstIterator it = entry.subentries ()->begin ();
+ it != entry.subentries ()->end (); ++it)
+ {
+ if (pos == last)
+ indent[level - 1] = '\\';
+ dumpTree ((*it), level, indent, file);
+ ++pos;
+ }
+ if (level > 1)
+ {
+ indent[level - 2] = savindent[0];
+ indent[level - 1] = savindent[1];
+ }
+ if (extra_ind)
+ --level;
+ indent[level] = '\0';
+}
+
+void dumpTree (FILE * file)
+{
+ char indent[1024];
+ indent[0] = '\0';
+ for (TreeList::ConstIterator it = treeList->begin ();
+ it != treeList->end (); ++it)
+ dumpTree (*it, 0, indent, file);
+}
+
+void createTree (const QCString & treefile, int threshold, int maxdepth)
+{
+ FILE * file = fopen (treefile, "w");
+ if (file == NULL)
+ {
+ fprintf (stderr, "Can't write tree file.\n");
+ return;
+ }
+ treeList = new TreeList;
+ buildTree ();
+ processTree (treeList, threshold, maxdepth, 0);
+ dumpTree (file);
+ fclose (file);
+}
+
+void readExcludeFile(const char *name)
+{
+ FILE *stream = fopen(name, "r");
+ if (!stream)
+ {
+ fprintf(stderr, "Error: Can't open %s.\n", name);
+ exit(1);
+ }
+ char line[1024];
+ while(!feof(stream))
+ {
+ if (!fgets(line, 1023, stream)) break;
+ if ((line[0] == 0) || (line[0] == '#')) continue;
+ line[strlen(line)-1] = 0;
+ excludes->append(line);
+ }
+ fclose(stream);
+ excludes->sort();
+}
+
+static KCmdLineOptions options[] =
+{
+ { "x", 0, 0 },
+ { "exclude <file>", "File containing symbols to exclude from output", 0},
+ { "e", 0, 0 },
+ { "exe <file>", "Executable to use for looking up unknown symbols", 0},
+ { "+<trace-log>", "Log file to investigate", 0},
+ {"t", 0, 0},
+ {"tree <file>", "File to write allocations tree", 0},
+ {"th", 0, 0},
+ {"treethreshold <value>",
+ "Don't print subtrees which allocated less than <value> memory", 0},
+ {"td", 0, 0},
+ {"treedepth <value>",
+ "Don't print subtrees that are deeper than <value>", 0},
+ KCmdLineLastOption
+};
+
+int main(int argc, char *argv[])
+{
+ KInstance instance("kmtrace");
+
+ KCmdLineArgs::init(argc, argv, "kmtrace", "KDE Memory leak tracer", "v1.0");
+
+ KCmdLineArgs::addCmdLineOptions(options);
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ (void) args->count();
+ const char *logfile;
+ if(args->count())
+ logfile = args->arg(0);
+ else
+ logfile = "ktrace.out";
+
+ QCString exe = args->getOption("exe");
+ QCString exclude;
+
+ excludes = new QStrList;
+
+ exclude = QFile::encodeName(locate("data", "kmtrace/kde.excludes"));
+ if(!exclude.isEmpty())
+ readExcludeFile(exclude);
+
+ exclude = args->getOption("exclude");
+ if (!exclude.isEmpty())
+ {
+ fprintf(stderr, "Reading %s\n", exclude.data());
+ readExcludeFile(exclude);
+ }
+
+ FILE *stream = fopen(logfile, "r");
+ if (!stream)
+ {
+ fprintf(stderr, "Can't open %s\n", logfile);
+ exit(1);
+ }
+
+ entryDict = new QIntDict<Entry>(9973);
+ symbolDict = new QIntDict<char>(9973);
+ formatDict = new QIntDict<char>(9973);
+ entryList = new QSortedList<Entry>;
+
+ fprintf(stderr, "Running\n");
+ QCString line;
+ char line2[1024];
+ while(!feof(stream))
+ {
+ fgets(line2, 1023, stream);
+ line2[strlen(line2)-1] = 0;
+ if (line2[0] == '=')
+ {
+ printf("%s\n", line2);
+ if( strcmp( line2, "= End" ) == 0 )
+ break;
+ }
+ else if (line2[0] == '#')
+ {
+ QCString app(line2+1);
+ if(exe.isEmpty())
+ {
+ exe = app.stripWhiteSpace();
+ fprintf(stderr, "ktrace.out: malloc trace of %s\n", exe.data());
+ }
+ else if(!app.contains(exe.data()))
+ {
+ fprintf(stderr, "trace file was for application '%s', not '%s'\n", app.data(), exe.data());
+ exit(1);
+ }
+ }
+ else if (line2[0] == '@')
+ line = 0;
+ else if (line2[0] == '[')
+ line = line + ' ' + line2;
+ else if (line2[0] == '+')
+ {
+ allocCount++;
+ line = line + ' ' + line2;
+ parseLine(line, '+');
+ line = 0;
+ if (allocCount & 128)
+ {
+ fprintf(stderr, "\rTotal long term allocs: %d still allocated: %d ", allocCount, entryDict->count());
+ }
+ }
+ else if (line2[0] == '-')
+ {
+ line = line + ' ' + line2;
+ parseLine(line, '-');
+ line = 0;
+ }
+ else if (line2[0] == '<')
+ {
+ line2[0] = '-';
+ // First part of realloc (free)
+ QCString reline = line + ' ' + line2;
+ parseLine(reline, '-');
+ }
+ else if (line2[0] == '>')
+ {
+ line2[0] = '+';
+ // Second part of realloc (alloc)
+ line = line + ' ' + line2;
+ parseLine(line, '+');
+ line = 0;
+ }
+ else
+ {
+ char *addr = index(line2,'[');
+ if (addr)
+ {
+ line = line + ' ' + addr;
+ }
+ }
+ }
+ leakedCount = count;
+ fprintf(stderr, "\rTotal long term allocs: %d still allocated: %d(%d) \n", allocCount, leakedCount, entryDict->count());
+ printf("Totals allocated: %d bytes in %d blocks.\n", totalBytesAlloced, allocCount);
+ printf("Maximum allocated: %d bytes / %d blocks.\n", maxBytes, maxCount);
+ fprintf(stderr, "Collecting duplicates...\n");
+ collectDupes();
+ fprintf(stderr, "Sorting...\n");
+ sortBlocks();
+ printf("Totals leaked: %d bytes in %d blocks.\n", totalBytesLeaked, leakedCount);
+ fprintf(stderr, "Looking up symbols...\n");
+ rewind(stream);
+ lookupSymbols(stream);
+ fprintf(stderr, "Looking up unknown symbols...\n");
+ lookupUnknownSymbols(exe);
+ fprintf(stderr, "Printing...\n");
+ dumpBlocks();
+ QCString treeFile = args->getOption ("tree");
+ if (!treeFile.isEmpty ())
+ {
+ fprintf (stderr, "Creating allocation tree...\n");
+ createTree (treeFile, args->getOption ("treethreshold").toInt (),
+ args->getOption ("treedepth").toInt ());
+ }
+ fprintf(stderr, "Done.\n");
+ return 0;
+}
diff --git a/kmtrace/ksotrace.cpp b/kmtrace/ksotrace.cpp
new file mode 100644
index 00000000..9f379414
--- /dev/null
+++ b/kmtrace/ksotrace.cpp
@@ -0,0 +1,11 @@
+#include "ktrace.h"
+#include <stdlib.h>
+
+class KTraceActivate
+{
+public:
+ KTraceActivate() { setenv("LD_PRELOAD","",1); ktrace(); }
+ ~KTraceActivate() { kuntrace(); }
+} kTraceActivateInstance;
+
+
diff --git a/kmtrace/ktrace.c b/kmtrace/ktrace.c
new file mode 100644
index 00000000..044a1d24
--- /dev/null
+++ b/kmtrace/ktrace.c
@@ -0,0 +1,840 @@
+/* More debugging hooks for `malloc'.
+ Copyright (C) 1991,92,93,94,96,97,98,99,2000 Free Software Foundation, Inc.
+ Written April 2, 1991 by John Gilmore of Cygnus Support.
+ Based on mcheck.c by Mike Haertel.
+ Hacked by AK
+ Cleanup and performance improvements by
+ Chris Schlaeger <cs@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation.
+*/
+
+#define MALLOC_HOOKS
+#define _GNU_SOURCE
+
+#include <pthread.h>
+#include <malloc.h>
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <execinfo.h>
+#include <unistd.h>
+
+#ifdef USE_IN_LIBIO
+# include <libio/iolibio.h>
+# define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
+#endif
+
+/* This is the most important parameter. It should be set to two times
+ * the maximum number of mallocs the application uses at a time. Prime
+ * numbers are very good candidates for this value. I added a list of
+ * some prime numbers for conveniance.
+ *
+ * 10007, 20011, 30013, 40031, 50033, 60037, 70039, 80051, 90053,
+ * 100057, 110059, 120067, 130069, 140071, 150077, 160079, 170081,
+ * 180097, 190121, 200131, 210139, 220141, 230143, 240151, 250153,
+ * 260171, 270191, 280199, 290201, 300221, 310223, 320237, 330241,
+ * 340261, 350281, 360287, 370373, 380377, 390389 */
+#define TR_CACHE_SIZE 100057
+
+/* The DELTA value is also a value for the maximum
+ * number of iterations during a positive free/realloc
+ * search. It must NOT divide TR_CACHE_SIZE without
+ * remainder! */
+#define DELTA 23
+
+/* The high and low mark control the flushing algorithm. Whenever the
+ * hashtable reaches the high mark every DELTAth entry is written to
+ * disk until the low filling mark is reached. A hash table becomes
+ * very inefficient when it becomes filled to 50% or more. */
+#define TR_HIGH_MARK ((int) (TR_CACHE_SIZE * 0.5))
+#define TR_LOW_MARK ((int) (TR_HIGH_MARK - (TR_CACHE_SIZE / DELTA)))
+
+/* Maximum call stack depth. No checking for overflows
+ * is done. Adjust this value with care! */
+#define TR_BT_SIZE 100
+
+#define PROFILE 1
+
+/* The hash function. Since the smallest allocated block is probably
+ * not smaller than 8 bytes we ignore the last 3 LSBs. */
+#define HASHFUNC(a) (((((unsigned long) a) << 1) ^ \
+ (((unsigned long) a) >> 3)) % \
+ TR_CACHE_SIZE)
+
+#define TR_HASHTABLE_SIZE 9973
+
+#define TR_NONE 0
+#define TR_MALLOC 1
+#define TR_REALLOC 2
+#define TR_FREE 3
+
+#define TRACE_BUFFER_SIZE 512
+
+typedef struct
+{
+ void* ptr;
+ size_t size;
+ int bt_size;
+ void** bt;
+} tr_entry;
+
+typedef struct CallerNode
+{
+ void* funcAdr;
+ unsigned long mallocs;
+ unsigned long mallocsSum;
+ unsigned int noCallees;
+ unsigned int maxCallees;
+ struct CallerNode** callees;
+} CallerNode;
+
+void ktrace(void);
+void kuntrace(void);
+
+static void addAllocationToTree(void);
+
+static void tr_freehook __P ((void*, const void*));
+static void* tr_reallochook __P ((void*, size_t,
+ const void*));
+static void* tr_mallochook __P ((size_t, const void*));
+/* Old hook values. */
+static void (*tr_old_free_hook) __P ((void* ptr, const void*));
+static void* (*tr_old_malloc_hook) __P ((size_t size,
+ const void*));
+static void* (*tr_old_realloc_hook) __P ((void* ptr,
+ size_t size,
+ const void*));
+
+static FILE* mallstream;
+static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
+
+
+/* Address to breakpoint on accesses to... */
+void* mallwatch;
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+
+static int bt_size;
+static void *bt[TR_BT_SIZE + 1];
+static char tr_offsetbuf[20];
+static tr_entry tr_cache[TR_CACHE_SIZE];
+static int tr_cache_level;
+static int tr_cache_pos;
+static int tr_max_offset = 0;
+static void *tr_hashtable[TR_HASHTABLE_SIZE];
+#ifdef PROFILE
+static unsigned long tr_mallocs = 0;
+static unsigned long tr_logged_mallocs = 0;
+static unsigned long tr_frees = 0;
+static unsigned long tr_logged_frees = 0;
+static unsigned long tr_current_mallocs = 0;
+static unsigned long tr_max_mallocs = 0;
+static unsigned long tr_flashes = 0;
+static unsigned long tr_failed_free_lookups = 0;
+static unsigned long tr_malloc_collisions = 0;
+#endif
+
+static CallerNode* CallTree = NULL;
+static char* mallTreeFile = NULL;
+static FILE* mallTreeStream = NULL;
+static long mallThreshold = 2000;
+
+/* This function is called when the block being alloc'd, realloc'd, or
+ * freed has an address matching the variable "mallwatch". In a
+ * debugger, set "mallwatch" to the address of interest, then put a
+ * breakpoint on tr_break. */
+void tr_break __P ((void));
+void
+tr_break()
+{
+}
+
+__inline__ static void
+tr_backtrace(void **_bt, int size)
+{
+ int i;
+ Dl_info info;
+ for (i = 0; i < size; i++)
+ {
+ long hash = (((unsigned long)_bt[i]) / 4) % TR_HASHTABLE_SIZE;
+ if ((tr_hashtable[hash]!= _bt[i]) && dladdr(_bt[i], &info) &&
+ info.dli_fname && *info.dli_fname)
+ {
+ if (_bt[i] >= (void *) info.dli_saddr)
+ sprintf(tr_offsetbuf, "+%#lx", (unsigned long)
+ (_bt[i] - info.dli_saddr));
+ else
+ sprintf(tr_offsetbuf, "-%#lx", (unsigned long)
+ (info.dli_saddr - _bt[i]));
+ fprintf(mallstream, "%s%s%s%s%s[%p]\n",
+ info.dli_fname ?: "",
+ info.dli_sname ? "(" : "",
+ info.dli_sname ?: "",
+ info.dli_sname ? tr_offsetbuf : "",
+ info.dli_sname ? ")" : "",
+ _bt[i]);
+ tr_hashtable[hash] = _bt[i];
+ }
+ else
+ {
+ fprintf(mallstream, "[%p]\n", _bt[i]);
+ }
+ }
+}
+
+__inline__ static void
+tr_log(const void* caller, void* ptr, void* old,
+ size_t size, int op)
+{
+ int i, offset;
+
+ switch (op)
+ {
+ case TR_FREE:
+ i = HASHFUNC(ptr);
+ if ((offset = (i + tr_max_offset + 1)) >= TR_CACHE_SIZE)
+ offset -= TR_CACHE_SIZE;
+ do
+ {
+ if (tr_cache[i].ptr == ptr)
+ {
+ tr_cache[i].ptr = NULL;
+ free(tr_cache[i].bt);
+ tr_cache_level--;
+ return;
+ }
+ if (++i >= TR_CACHE_SIZE)
+ i = 0;
+#ifdef PROFILE
+ tr_failed_free_lookups++;
+#endif
+ } while (i != offset);
+
+ /* We don't know this allocation, so it has been flushed to disk
+ * already. So flush free as well. */
+ fprintf(mallstream, "@\n");
+ bt_size = backtrace(bt, TR_BT_SIZE);
+ tr_backtrace(&(bt[1]), bt_size - 2);
+ fprintf(mallstream, "- %p\n", ptr);
+#ifdef PROFILE
+ tr_logged_frees++;
+#endif
+ return;
+
+ case TR_REALLOC:
+ /* If old is 0 it's actually a malloc. */
+ if (old)
+ {
+ i = HASHFUNC(old);
+ if ((offset = (i + tr_max_offset + 1)) >= TR_CACHE_SIZE)
+ offset -= TR_CACHE_SIZE;
+ do
+ {
+ if (tr_cache[i].ptr == old)
+ {
+ int j = HASHFUNC(ptr);
+ /* We move the entry otherwise the free will be
+ * fairly expensive due to the wrong place in the
+ * hash table. */
+ tr_cache[i].ptr = NULL;
+ for ( ; ; )
+ {
+ if (tr_cache[j].ptr == NULL)
+ break;
+
+ if (++j >= TR_CACHE_SIZE)
+ i = 0;
+ }
+ tr_cache[j].ptr = ptr;
+ if (ptr)
+ {
+ tr_cache[j].size = tr_cache[i].size;
+ tr_cache[j].bt_size = tr_cache[i].bt_size;
+ tr_cache[j].bt = tr_cache[i].bt;
+ }
+ else
+ tr_cache_level--;
+ tr_cache[i].size = size;
+ return;
+ }
+ if (++i >= TR_CACHE_SIZE)
+ i = 0;
+ } while (i != offset);
+
+ fprintf(mallstream, "@\n");
+ bt_size = backtrace(bt, TR_BT_SIZE);
+ tr_backtrace(&(bt[1]), bt_size - 2);
+ fprintf(mallstream, "< %p\n", old);
+ fprintf(mallstream, "> %p %#lx\n", ptr,
+ (unsigned long) size);
+ return;
+ }
+
+ case TR_MALLOC:
+ if (tr_cache_level >= TR_HIGH_MARK)
+ {
+ /* The hash table becomes ineffective when the high mark has
+ * been reached. We still need some more experience with
+ * the low mark. It's unclear what reasonable values are. */
+#ifdef PROFILE
+ tr_flashes++;
+#endif
+ i = HASHFUNC(ptr);
+ do
+ {
+ if (tr_cache[i].ptr)
+ {
+#ifdef PROFILE
+ tr_logged_mallocs++;
+#endif
+ fprintf(mallstream, "@\n");
+ tr_backtrace(&(tr_cache[i].bt[1]),
+ tr_cache[i].bt_size - 2);
+ fprintf(mallstream, "+ %p %#lx\n",
+ tr_cache[i].ptr,
+ (unsigned long int)
+ tr_cache[i].size);
+ tr_cache[i].ptr = NULL;
+ tr_cache_level--;
+ }
+ if ((i += DELTA) >= TR_CACHE_SIZE)
+ i -= TR_CACHE_SIZE;
+ } while (tr_cache_level > TR_LOW_MARK);
+ }
+
+ i = HASHFUNC(ptr);
+ for ( ; ; )
+ {
+ if (tr_cache[i].ptr == NULL)
+ break;
+
+ if (++i >= TR_CACHE_SIZE)
+ i = 0;
+#ifdef PROFILE
+ tr_malloc_collisions++;
+#endif
+ }
+ if ((offset = (i - HASHFUNC(ptr))) < 0)
+ offset += TR_CACHE_SIZE;
+ if (offset > tr_max_offset)
+ tr_max_offset = offset;
+
+ tr_cache[i].ptr = ptr;
+ tr_cache[i].size = size;
+ tr_cache[i].bt = (void**) malloc(TR_BT_SIZE * sizeof(void*));
+ tr_cache[i].bt_size = backtrace(
+ tr_cache[i].bt, TR_BT_SIZE);
+ tr_cache[i].bt = realloc(tr_cache[i].bt, tr_cache[i].bt_size * sizeof(void*));
+ tr_cache_level++;
+
+ return;
+
+ case TR_NONE:
+ if (tr_cache[tr_cache_pos].ptr)
+ {
+#ifdef PROFILE
+ tr_logged_mallocs++;
+#endif
+ fprintf(mallstream, "@\n");
+ tr_backtrace(&(tr_cache[tr_cache_pos].bt[1]),
+ tr_cache[tr_cache_pos].bt_size - 2);
+ fprintf(mallstream, "+ %p %#lx\n",
+ tr_cache[tr_cache_pos].ptr,
+ (unsigned long int)
+ tr_cache[tr_cache_pos].size);
+ tr_cache[tr_cache_pos].ptr = NULL;
+ free(tr_cache[tr_cache_pos].bt);
+ tr_cache_level--;
+ }
+
+ if (++tr_cache_pos >= TR_CACHE_SIZE)
+ tr_cache_pos = 0;
+ break;
+ }
+}
+
+static void
+tr_freehook (ptr, caller)
+ void* ptr;
+ const void* caller;
+{
+ if (ptr == NULL)
+ return;
+ if (ptr == mallwatch)
+ tr_break ();
+
+ pthread_mutex_lock(&lock);
+#ifdef PROFILE
+ tr_frees++;
+ tr_current_mallocs--;
+#endif
+
+ __free_hook = tr_old_free_hook;
+
+ if (tr_old_free_hook != NULL)
+ (*tr_old_free_hook) (ptr, caller);
+ else
+ free(ptr);
+ tr_log(caller, ptr, 0, 0, TR_FREE);
+
+ __free_hook = tr_freehook;
+ pthread_mutex_unlock(&lock);
+}
+
+static void*
+tr_mallochook (size, caller)
+ size_t size;
+ const void* caller;
+{
+ void* hdr;
+
+ pthread_mutex_lock(&lock);
+
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+ __free_hook = tr_old_free_hook;
+
+ if (tr_old_malloc_hook != NULL)
+ hdr = (void*) (*tr_old_malloc_hook) (size, caller);
+ else
+ hdr = (void*) malloc(size);
+ tr_log(caller, hdr, 0, size, TR_MALLOC);
+ /* We only build the allocation tree if mallTreeFile has been set. */
+ if (mallTreeFile)
+ addAllocationToTree();
+
+ __malloc_hook = tr_mallochook;
+ __realloc_hook = tr_reallochook;
+ __free_hook = tr_freehook;
+
+#ifdef PROFILE
+ tr_mallocs++;
+ tr_current_mallocs++;
+ if (tr_current_mallocs > tr_max_mallocs)
+ tr_max_mallocs = tr_current_mallocs;
+#endif
+ pthread_mutex_unlock(&lock);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return hdr;
+}
+
+static void*
+tr_reallochook (ptr, size, caller)
+ void* ptr;
+ size_t size;
+ const void* caller;
+{
+ void* hdr;
+
+ if (ptr == mallwatch)
+ tr_break ();
+
+ pthread_mutex_lock(&lock);
+
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+
+ if (tr_old_realloc_hook != NULL)
+ hdr = (void*) (*tr_old_realloc_hook) (ptr, size, caller);
+ else
+ hdr = (void*) realloc (ptr, size);
+
+ tr_log(caller, hdr, ptr, size, TR_REALLOC);
+
+ __free_hook = tr_freehook;
+ __malloc_hook = tr_mallochook;
+ __realloc_hook = tr_reallochook;
+
+#ifdef PROFILE
+ /* If ptr is 0 there was no previos malloc of this location */
+ if (ptr == NULL)
+ {
+ tr_mallocs++;
+ tr_current_mallocs++;
+ if (tr_current_mallocs > tr_max_mallocs)
+ tr_max_mallocs = tr_current_mallocs;
+ }
+#endif
+
+ pthread_mutex_unlock(&lock);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return hdr;
+}
+
+void
+addAllocationToTree(void)
+{
+ int bt_size;
+ int i, j;
+ void *bt[TR_BT_SIZE + 1];
+ CallerNode* cn = CallTree;
+ CallerNode** parent = &CallTree;
+
+ bt_size = backtrace(bt, TR_BT_SIZE);
+ for (i = bt_size - 1; i >= 4; i--)
+ {
+ if (cn == NULL)
+ {
+ *parent = cn = (CallerNode*) malloc(sizeof(CallerNode));
+ cn->funcAdr = bt[i];
+ cn->mallocs = 0;
+ cn->noCallees = 0;
+ cn->maxCallees = 0;
+ cn->callees = NULL;
+ }
+ if (i == 4)
+ cn->mallocs++;
+ else
+ {
+ int knownCallee = 0;
+ for (j = 0; j < cn->noCallees; j++)
+ if (bt[i - 1] == cn->callees[j]->funcAdr)
+ {
+ parent = &cn->callees[j];
+ cn = cn->callees[j];
+ knownCallee = 1;
+ break;
+ }
+ if (!knownCallee)
+ {
+ if (cn->noCallees == cn->maxCallees)
+ {
+ /* Copy callees into new, larger array. */
+ CallerNode** tmp;
+ int newSize = 2 * cn->maxCallees;
+ if (newSize == 0)
+ newSize = 4;
+ tmp = (CallerNode**) malloc(newSize * sizeof(CallerNode*));
+ memcpy(tmp, cn->callees,
+ cn->maxCallees * sizeof(CallerNode*));
+ if (cn->callees)
+ free(cn->callees);
+ cn->callees = tmp;
+ memset(&cn->callees[cn->maxCallees], 0,
+ (newSize - cn->maxCallees) * sizeof(CallerNode*));
+ cn->maxCallees = newSize;
+ }
+ parent = &cn->callees[cn->noCallees++];
+ cn = 0;
+ }
+ }
+ }
+}
+
+static int
+removeBranchesBelowThreshold(CallerNode* root)
+{
+ int i;
+ int max;
+
+ if (!root)
+ return (0);
+ for (i = 0; i < root->noCallees; i++)
+ {
+ if (removeBranchesBelowThreshold(root->callees[i]))
+ {
+ free(root->callees[i]);
+ if (root->noCallees > 1)
+ {
+ root->callees[i] = root->callees[root->noCallees - 1];
+ root->callees[root->noCallees - 1] = 0;
+ }
+ else if (root->noCallees == 1)
+ root->callees[i] = 0;
+
+ root->noCallees--;
+ i--;
+ }
+ }
+ if (root->noCallees == 0 && root->mallocs < mallThreshold )
+ return (1);
+
+ return (0);
+}
+
+static void
+dumpCallTree(CallerNode* root, char* indentStr, int rawMode)
+{
+ int i;
+ Dl_info info;
+ char* newIndentStr;
+ size_t indDepth;
+
+ if (!root || !mallTreeStream)
+ return;
+
+ if (rawMode)
+ {
+ fprintf(mallTreeStream, "-");
+ }
+ else
+ {
+ newIndentStr = (char*) malloc(strlen(indentStr) + 2);
+ strcpy(newIndentStr, indentStr);
+ if (root->noCallees > 0)
+ strcat(newIndentStr, "+");
+ indDepth = strlen(newIndentStr);
+ fprintf(mallTreeStream, "%s- ", newIndentStr);
+ }
+
+ if (dladdr(root->funcAdr, &info) && info.dli_fname && *info.dli_fname)
+ {
+ if (root->funcAdr >= (void *) info.dli_saddr)
+ sprintf(tr_offsetbuf, "+%#lx", (unsigned long)
+ (root->funcAdr - info.dli_saddr));
+ else
+ sprintf(tr_offsetbuf, "-%#lx", (unsigned long)
+ (info.dli_saddr - root->funcAdr));
+ fprintf(mallTreeStream, "%s%s%s%s%s[%p]",
+ info.dli_fname ?: "",
+ info.dli_sname ? "(" : "",
+ info.dli_sname ?: "",
+ info.dli_sname ? tr_offsetbuf : "",
+ info.dli_sname ? ")" : "",
+ root->funcAdr);
+ }
+ else
+ {
+ fprintf(mallTreeStream, "[%p]", root->funcAdr);
+ }
+ fprintf(mallTreeStream, ": %lu\n", root->mallocs);
+ if (indDepth > 1 && !rawMode)
+ {
+ if (newIndentStr[indDepth - 2] == '+')
+ newIndentStr[indDepth - 2] = '|';
+ else if (newIndentStr[indDepth - 2] == '\\')
+ newIndentStr[indDepth - 2] = ' ';
+ }
+
+ for (i = 0; i < root->noCallees; i++)
+ {
+ if (rawMode)
+ dumpCallTree(root->callees[i], "", 1);
+ else
+ {
+ if (i == root->noCallees - 1)
+ newIndentStr[indDepth - 1] = '\\';
+ dumpCallTree(root->callees[i], newIndentStr, rawMode);
+ }
+ }
+ if (rawMode)
+ fprintf(mallTreeStream, ".\n");
+ else
+ free(newIndentStr);
+
+}
+
+#ifdef _LIBC
+extern void __libc_freeres (void);
+
+/* This function gets called to make sure all memory the library
+ * allocates get freed and so does not irritate the user when studying
+ * the mtrace output. */
+static void
+release_libc_mem (void)
+{
+ /* Only call the free function if we still are running in mtrace
+ * mode. */
+ /*if (mallstream != NULL)
+ __libc_freeres ();*/
+
+ kuntrace();
+ write(2, "kuntrace()\n", 11);
+}
+#endif
+
+/* We enable tracing if either the environment variable MALLOC_TRACE
+ * or the variable MALLOC_TREE are set, or if the variable mallwatch
+ * has been patched to an address that the debugging user wants us to
+ * stop on. When patching mallwatch, don't forget to set a breakpoint
+ * on tr_break! */
+void
+ktrace()
+{
+#ifdef _LIBC
+ static int added_atexit_handler;
+#endif
+ char* mallfile;
+
+ /* Don't panic if we're called more than once. */
+ if (mallstream != NULL)
+ return;
+
+#ifdef _LIBC
+ /* When compiling the GNU libc we use the secure getenv function
+ * which prevents the misuse in case of SUID or SGID enabled
+ * programs. */
+ mallfile = __secure_getenv("MALLOC_TRACE");
+ mallTreeFile = __secure_getenv("MALLOC_TREE");
+ if( __secure_getenv("MALLOC_THRESHOLD") != NULL )
+ mallThreshold = atol(__secure_getenv("MALLOC_THRESHOLD"));
+#else
+ mallfile = getenv("MALLOC_TRACE");
+ mallTreeFile = getenv("MALLOC_TREE");
+ if( getenv("MALLOC_THRESHOLD") != NULL )
+ mallThreshold = atol(getenv("MALLOC_THRESHOLD"));
+#endif
+ if (mallfile != NULL || mallTreeFile != NULL || mallwatch != NULL)
+ {
+ mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
+ if (mallstream != NULL)
+ {
+ char buf[512];
+
+ /* Be sure it doesn't malloc its buffer! */
+ setvbuf (mallstream, malloc_trace_buffer, _IOFBF,
+ TRACE_BUFFER_SIZE);
+ fprintf (mallstream, "= Start\n");
+ memset(buf, 0, sizeof(buf));
+ readlink("/proc/self/exe", buf, sizeof(buf));
+ if(*buf)
+ fprintf (mallstream, "#%s\n", buf);
+
+ /* Save old hooks and hook in our own functions for all
+ * malloc, realloc and free calls */
+ tr_old_free_hook = __free_hook;
+ __free_hook = tr_freehook;
+ tr_old_malloc_hook = __malloc_hook;
+ __malloc_hook = tr_mallochook;
+ tr_old_realloc_hook = __realloc_hook;
+ __realloc_hook = tr_reallochook;
+
+ tr_cache_pos = TR_CACHE_SIZE;
+ do
+ {
+ tr_cache[--tr_cache_pos].ptr = NULL;
+ } while (tr_cache_pos);
+ tr_cache_level = 0;
+
+ memset(tr_hashtable, 0, sizeof(void*) * TR_HASHTABLE_SIZE);
+#ifdef _LIBC
+ if (!added_atexit_handler)
+ {
+ added_atexit_handler = 1;
+ atexit (release_libc_mem);
+ }
+#endif
+ }
+ }
+}
+
+void
+kuntrace()
+{
+ if (mallstream == NULL)
+ return;
+
+ /* restore hooks to original values */
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+
+ if (removeBranchesBelowThreshold(CallTree))
+ CallTree = 0;
+ if (mallTreeFile)
+ {
+ if (mallTreeStream = fopen(mallTreeFile, "w"))
+ {
+ dumpCallTree(CallTree, "", 0);
+ fclose(mallTreeStream);
+ }
+ }
+
+ /* Flush cache. */
+ while (tr_cache_level)
+ tr_log(NULL, 0, 0, 0, TR_NONE);
+
+ fprintf (mallstream, "= End\n");
+#ifdef PROFILE
+ fprintf(mallstream, "\nMax Mallocs: %8ld Cache Size: %8ld"
+ " Flashes: %8ld\n"
+ "Mallocs: %8ld Frees: %8ld Leaks: %8ld\n"
+ "Logged Mallocs: %8ld Logged Frees: %8ld Logged Leaks: %8ld\n"
+ "Avg. Free lookups: %ld Malloc collisions: %ld Max offset: %ld\n",
+ tr_max_mallocs, TR_CACHE_SIZE, tr_flashes,
+ tr_mallocs, tr_frees, tr_current_mallocs,
+ tr_logged_mallocs, tr_logged_frees,
+ tr_logged_mallocs - tr_logged_frees,
+ tr_frees > 0 ? ( tr_failed_free_lookups / tr_frees ) : 0,
+ tr_malloc_collisions, tr_max_offset);
+#endif
+ fclose (mallstream);
+ mallstream = NULL;
+ write(2, "kuntrace()\n", 11);
+}
+
+int fork()
+{
+ int result;
+ if (mallstream)
+ fflush(mallstream);
+ result = __fork();
+ if (result == 0)
+ {
+ if (mallstream)
+ {
+ close(fileno(mallstream));
+ mallstream = NULL;
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+ }
+ }
+ return result;
+}
+
+
+static int my_mcount_lock = 0;
+void mcount()
+{
+ Dl_info info;
+ int i = 1;
+ if (my_mcount_lock) return;
+ my_mcount_lock = 1;
+ bt_size = backtrace(bt, TR_BT_SIZE);
+#if 0
+for(i = 1; (i < 5) && (i < bt_size); i++)
+{
+#endif
+ if (dladdr(bt[i], &info) && info.dli_fname && *info.dli_fname)
+ {
+ fprintf(stdout, "%s\n", info.dli_sname ? info.dli_sname : "");
+ }
+ else
+ {
+ fprintf(stdout, "[%p]\n", bt[i]);
+ }
+#if 0
+}
+ fprintf(stdout, "\n");
+#endif
+ my_mcount_lock = 0;
+}
+
diff --git a/kmtrace/ktrace.h b/kmtrace/ktrace.h
new file mode 100644
index 00000000..366f2148
--- /dev/null
+++ b/kmtrace/ktrace.h
@@ -0,0 +1,10 @@
+#ifndef _KTRACE_H
+#define _KTRACE_H
+
+extern "C" {
+/* Activate a standard collection of tracing hooks. */
+extern void ktrace (void);
+extern void kuntrace (void);
+}
+
+#endif /* ktrace.h */
diff --git a/kmtrace/match.cpp b/kmtrace/match.cpp
new file mode 100644
index 00000000..2a9c74b4
--- /dev/null
+++ b/kmtrace/match.cpp
@@ -0,0 +1,73 @@
+#include <qintdict.h>
+#include <stdio.h>
+#include <qstringlist.h>
+#include <qstrlist.h>
+#include <qtextstream.h>
+#include <qsortedlist.h>
+#include <qfile.h>
+#include <qtl.h>
+#include <qvaluelist.h>
+#include <stdlib.h>
+#include <ktempfile.h>
+#include <kinstance.h>
+#include <kstandarddirs.h>
+#include <kcmdlineargs.h>
+
+int main(int argc, char **argv)
+{
+ char buf[1024];
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: kmmatch <map-file> <call-file>\n");
+ fprintf(stderr, "\n<map-file> is a file as output by 'nm'.\n");
+ fprintf(stderr, "<call-file> is a file that contains symbols, e.g. a list of all\n"
+ "function calls made by a program.\n");
+ fprintf(stderr, "The program will print all symbols from <call-file> that are present\n"
+ "in <map-file>, in the same order as they appear in <call-file>.\n");
+ return 1;
+ }
+
+ int i = 1;
+ QDict<int> dict(20011);
+
+ FILE *map_file = fopen(argv[1], "r");
+ if (!map_file)
+ {
+ fprintf(stderr, "Error opening '%s'\n", argv[1]);
+ return 1;
+ }
+ while(!feof(map_file))
+ {
+ fgets(buf, 1024, map_file);
+ QString line = QString::fromLatin1(buf).stripWhiteSpace();
+ QStringList split = QStringList::split(' ', line);
+ if (split.count() <= 1)
+ return 1;
+
+ if (split[1] == "T")
+ {
+ dict.insert(split[2], &i);
+ }
+ }
+ fclose(map_file);
+
+ FILE *call_file = fopen(argv[2], "r");
+ if (!call_file)
+ {
+ fprintf(stderr, "Error opening '%s'\n", argv[2]);
+ return 1;
+ }
+
+ while(!feof(call_file))
+ {
+ fgets(buf, 1024, call_file);
+ QString line = QString::fromLatin1(buf).stripWhiteSpace();
+ if (dict.find(line))
+ {
+ qWarning("%s", line.latin1());
+ }
+ }
+ fclose(call_file);
+ return 0;
+}
+
diff --git a/kmtrace/mtrace.c b/kmtrace/mtrace.c
new file mode 100644
index 00000000..26c08ae6
--- /dev/null
+++ b/kmtrace/mtrace.c
@@ -0,0 +1,383 @@
+/* More debugging hooks for `malloc'.
+ Copyright (C) 1991,92,93,94,96,97,98,99,2000 Free Software Foundation, Inc.
+ Written April 2, 1991 by John Gilmore of Cygnus Support.
+ Based on mcheck.c by Mike Haertel.
+ Hacked by AK
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ The author may be reached (Email) at the address mike@ai.mit.edu,
+ or (US mail) as Mike Haertel c/o Free Software Foundation. */
+
+#define _LIBC
+#define MALLOC_HOOKS
+
+#ifndef _MALLOC_INTERNAL
+#define _MALLOC_INTERNAL
+#include <malloc.h>
+#include <mcheck.h>
+#include <bits/libc-lock.h>
+#endif
+
+#include <dlfcn.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <stdio-common/_itoa.h>
+#include <elf/ldsodefs.h>
+
+#ifdef USE_IN_LIBIO
+# include <libio/iolibio.h>
+# define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
+#endif
+
+#define TRACE_BUFFER_SIZE 512
+
+static FILE *mallstream;
+static const char mallenv[]= "MALLOC_TRACE";
+static char malloc_trace_buffer[TRACE_BUFFER_SIZE];
+
+__libc_lock_define_initialized (static, lock)
+
+/* Address to breakpoint on accesses to... */
+__ptr_t mallwatch;
+
+/* File name and line number information, for callers that had
+ the foresight to call through a macro. */
+char *_mtrace_file;
+int _mtrace_line;
+
+/* Old hook values. */
+static void (*tr_old_free_hook) __P ((__ptr_t ptr, const __ptr_t));
+static __ptr_t (*tr_old_malloc_hook) __P ((__malloc_size_t size,
+ const __ptr_t));
+static __ptr_t (*tr_old_realloc_hook) __P ((__ptr_t ptr,
+ __malloc_size_t size,
+ const __ptr_t));
+
+#define TR_PIPELINE_SIZE 16
+#define TR_BT_SIZE 80
+#define TR_HASHTABLE_SIZE 9973
+
+#define TR_NONE 0
+#define TR_MALLOC 1
+#define TR_REALLOC 2
+#define TR_FREE 3
+
+typedef struct {
+ int op;
+ __ptr_t ptr;
+ __ptr_t old;
+ __malloc_size_t size;
+ int bt_size;
+ void *bt[TR_BT_SIZE+1];
+} tr_entry;
+
+static tr_entry tr_pipeline[TR_PIPELINE_SIZE];
+static int tr_pipeline_pos;
+static void *tr_hashtable[TR_HASHTABLE_SIZE];
+
+
+/* This function is called when the block being alloc'd, realloc'd, or
+ freed has an address matching the variable "mallwatch". In a debugger,
+ set "mallwatch" to the address of interest, then put a breakpoint on
+ tr_break. */
+
+void tr_break __P ((void));
+void
+tr_break ()
+{
+}
+
+static void
+tr_backtrace(void **bt, int size)
+{
+ char buf[20];
+ int i;
+ Dl_info info;
+ for(i = 0; i < size; i++)
+ {
+ long hash = (((unsigned long)bt[i]) / 4) % TR_HASHTABLE_SIZE;
+ if ((tr_hashtable[hash]!= bt[i]) && _dl_addr(bt[i], &info) && info.dli_fname && *info.dli_fname)
+ {
+ if (bt[i] >= (void *) info.dli_saddr)
+ sprintf(buf, "+%#lx", (unsigned long)(bt[i] - info.dli_saddr));
+ else
+ sprintf(buf, "-%#lx", (unsigned long)(info.dli_saddr - bt[i]));
+ fprintf(mallstream, "%s%s%s%s%s[%p]\n",
+ info.dli_fname ?: "",
+ info.dli_sname ? "(" : "",
+ info.dli_sname ?: "",
+ info.dli_sname ? buf : "",
+ info.dli_sname ? ")" : "",
+ bt[i]);
+ tr_hashtable[hash] = bt[i];
+ }
+ else {
+ fprintf(mallstream, "[%p]\n", bt[i]);
+ }
+ }
+}
+
+static void
+inline
+tr_log(const __ptr_t caller, __ptr_t ptr, __ptr_t old, __malloc_size_t size, int op)
+{
+ switch(op)
+ {
+ case TR_REALLOC:
+ break;
+ case TR_MALLOC:
+ break;
+ case TR_FREE:
+ {
+ int i = tr_pipeline_pos;
+ do {
+ if (i) i--; else i = TR_PIPELINE_SIZE-1;
+ if (tr_pipeline[i].ptr == ptr)
+ {
+ if (tr_pipeline[i].op == TR_MALLOC)
+ {
+ tr_pipeline[i].op = TR_NONE;
+ tr_pipeline[i].ptr = NULL;
+ return;
+ }
+ break;
+ }
+ } while (i != tr_pipeline_pos);
+ }
+ }
+
+ if (tr_pipeline[tr_pipeline_pos].op)
+ {
+ putc('@', mallstream);
+ putc('\n', mallstream);
+ /* Generate backtrace...
+ * We throw out the first frame (tr_mallochook)
+ * end the last one (_start)
+ */
+ tr_backtrace(&(tr_pipeline[tr_pipeline_pos].bt[1]),
+ tr_pipeline[tr_pipeline_pos].bt_size-2);
+
+ switch(tr_pipeline[tr_pipeline_pos].op)
+ {
+ case TR_MALLOC:
+ fprintf (mallstream, "+ %p %#lx\n",
+ tr_pipeline[tr_pipeline_pos].ptr,
+ (unsigned long int) tr_pipeline[tr_pipeline_pos].size);
+ break;
+ case TR_FREE:
+ fprintf (mallstream, "- %p\n",
+ tr_pipeline[tr_pipeline_pos].ptr);
+ break;
+ case TR_REALLOC:
+ fprintf (mallstream, "< %p\n",
+ tr_pipeline[tr_pipeline_pos].old);
+ fprintf (mallstream, "> %p %#lx\n",
+ tr_pipeline[tr_pipeline_pos].ptr,
+ (unsigned long int) tr_pipeline[tr_pipeline_pos].size);
+ break;
+ }
+ }
+
+ tr_pipeline[tr_pipeline_pos].op = op;
+ tr_pipeline[tr_pipeline_pos].ptr = ptr;
+ tr_pipeline[tr_pipeline_pos].old = old;
+ tr_pipeline[tr_pipeline_pos].size = size;
+ tr_pipeline[tr_pipeline_pos].bt_size = backtrace(
+ tr_pipeline[tr_pipeline_pos].bt, TR_BT_SIZE);
+ tr_pipeline_pos++;
+ if (tr_pipeline_pos == TR_PIPELINE_SIZE)
+ tr_pipeline_pos = 0;
+}
+
+static void tr_freehook __P ((__ptr_t, const __ptr_t));
+static void
+tr_freehook (ptr, caller)
+ __ptr_t ptr;
+ const __ptr_t caller;
+{
+ if (ptr == NULL)
+ return;
+ __libc_lock_lock (lock);
+ tr_log(caller, ptr, 0, 0, TR_FREE );
+ __libc_lock_unlock (lock);
+ if (ptr == mallwatch)
+ tr_break ();
+ __libc_lock_lock (lock);
+ __free_hook = tr_old_free_hook;
+ if (tr_old_free_hook != NULL)
+ (*tr_old_free_hook) (ptr, caller);
+ else
+ free (ptr);
+ __free_hook = tr_freehook;
+ __libc_lock_unlock (lock);
+}
+
+static __ptr_t tr_mallochook __P ((__malloc_size_t, const __ptr_t));
+static __ptr_t
+tr_mallochook (size, caller)
+ __malloc_size_t size;
+ const __ptr_t caller;
+{
+ __ptr_t hdr;
+
+ __libc_lock_lock (lock);
+
+ __malloc_hook = tr_old_malloc_hook;
+ if (tr_old_malloc_hook != NULL)
+ hdr = (__ptr_t) (*tr_old_malloc_hook) (size, caller);
+ else
+ hdr = (__ptr_t) malloc (size);
+ __malloc_hook = tr_mallochook;
+
+ tr_log(caller, hdr, 0, size, TR_MALLOC);
+
+ __libc_lock_unlock (lock);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return hdr;
+}
+
+static __ptr_t tr_reallochook __P ((__ptr_t, __malloc_size_t, const __ptr_t));
+static __ptr_t
+tr_reallochook (ptr, size, caller)
+ __ptr_t ptr;
+ __malloc_size_t size;
+ const __ptr_t caller;
+{
+ __ptr_t hdr;
+
+ if (ptr == mallwatch)
+ tr_break ();
+
+ __libc_lock_lock (lock);
+
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+ if (tr_old_realloc_hook != NULL)
+ hdr = (__ptr_t) (*tr_old_realloc_hook) (ptr, size, caller);
+ else
+ hdr = (__ptr_t) realloc (ptr, size);
+ __free_hook = tr_freehook;
+ __malloc_hook = tr_mallochook;
+ __realloc_hook = tr_reallochook;
+
+ tr_log(caller, hdr, ptr, size, TR_REALLOC);
+
+ __libc_lock_unlock (lock);
+
+ if (hdr == mallwatch)
+ tr_break ();
+
+ return hdr;
+}
+
+
+#ifdef _LIBC
+extern void __libc_freeres (void);
+
+/* This function gets called to make sure all memory the library
+ allocates get freed and so does not irritate the user when studying
+ the mtrace output. */
+static void
+release_libc_mem (void)
+{
+ /* Only call the free function if we still are running in mtrace mode. */
+ if (mallstream != NULL)
+ __libc_freeres ();
+}
+#endif
+
+
+/* We enable tracing if either the environment variable MALLOC_TRACE
+ is set, or if the variable mallwatch has been patched to an address
+ that the debugging user wants us to stop on. When patching mallwatch,
+ don't forget to set a breakpoint on tr_break! */
+
+void
+mtrace ()
+{
+#ifdef _LIBC
+ static int added_atexit_handler;
+#endif
+ char *mallfile;
+
+ /* Don't panic if we're called more than once. */
+ if (mallstream != NULL)
+ return;
+
+#ifdef _LIBC
+ /* When compiling the GNU libc we use the secure getenv function
+ which prevents the misuse in case of SUID or SGID enabled
+ programs. */
+ mallfile = __secure_getenv (mallenv);
+#else
+ mallfile = getenv (mallenv);
+#endif
+ if (mallfile != NULL || mallwatch != NULL)
+ {
+ mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "w");
+ if (mallstream != NULL)
+ {
+ /* Be sure it doesn't malloc its buffer! */
+ setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
+ fprintf (mallstream, "= Start\n");
+ tr_old_free_hook = __free_hook;
+ __free_hook = tr_freehook;
+ tr_old_malloc_hook = __malloc_hook;
+ __malloc_hook = tr_mallochook;
+ tr_old_realloc_hook = __realloc_hook;
+ __realloc_hook = tr_reallochook;
+
+ tr_pipeline_pos = TR_PIPELINE_SIZE;
+ for(;tr_pipeline_pos--;)
+ {
+ tr_pipeline[tr_pipeline_pos].op = TR_NONE;
+ tr_pipeline[tr_pipeline_pos].ptr = NULL;
+ }
+ memset(tr_hashtable, 0, sizeof(void *)*TR_HASHTABLE_SIZE);
+#ifdef _LIBC
+ if (!added_atexit_handler)
+ {
+ added_atexit_handler = 1;
+ atexit (release_libc_mem);
+ }
+#endif
+ }
+ }
+}
+
+void
+muntrace ()
+{
+ if (mallstream == NULL)
+ return;
+
+ fprintf (mallstream, "= End\n");
+ fclose (mallstream);
+ mallstream = NULL;
+ __free_hook = tr_old_free_hook;
+ __malloc_hook = tr_old_malloc_hook;
+ __realloc_hook = tr_old_realloc_hook;
+}
+
+