Move trace-malloc readers from mozilla/xpcom/base/ to mozilla/tools/trace-malloc/. Add a --shutdown-leaks option to nsTraceMalloc to dump, to the file given as an argument to the option, information about allocations still live at shutdown. Add a new trace-malloc reader (leakstats.c) to print leak statistics. b=84831 r=jag sr=brendan
This commit is contained in:
Родитель
025d54da07
Коммит
03b8e91855
|
@ -154,6 +154,10 @@ ifdef ENABLE_TESTS
|
|||
DIRS += xpcom/tests
|
||||
endif
|
||||
|
||||
ifdef NS_TRACE_MALLOC
|
||||
DIRS += tools/trace-malloc
|
||||
endif
|
||||
|
||||
DIRS += l10n
|
||||
|
||||
ifneq (,$(MOZ_STATIC_COMPONENTS)$(MOZ_META_COMPONENTS))
|
||||
|
|
|
@ -913,6 +913,11 @@ if [ "$MOZ_LEAKY" ]; then
|
|||
MAKEFILES_leaky="tools/leaky/Makefile"
|
||||
fi
|
||||
|
||||
# tools/trace-malloc
|
||||
if [ "$NS_TRACE_MALLOC" ]; then
|
||||
MAKEFILES_tracemalloc="tools/trace-malloc/Makefile"
|
||||
fi
|
||||
|
||||
# layout/mathml
|
||||
if [ "$MOZ_MATHML" ]; then
|
||||
MAKEFILES_layout="$MAKEFILES_layout
|
||||
|
@ -1123,6 +1128,7 @@ $MAKEFILES_rdf
|
|||
$MAKEFILES_static_components
|
||||
$MAKEFILES_sun_java
|
||||
$MAKEFILES_themes
|
||||
$MAKEFILES_tracemalloc
|
||||
$MAKEFILES_uriloader
|
||||
$MAKEFILES_view
|
||||
$MAKEFILES_webshell
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#
|
||||
# The contents of this file are subject to the Netscape Public
|
||||
# License Version 1.1 (the "License"); you may not use this file
|
||||
# except in compliance with the License. You may obtain a copy of
|
||||
# the License at http://www.mozilla.org/NPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS
|
||||
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
||||
# implied. See the License for the specific language governing
|
||||
# rights and limitations under the License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is Netscape
|
||||
# Communications Corporation. Portions created by Netscape are
|
||||
# Copyright (C) 1998 Netscape Communications Corporation. All
|
||||
# Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
REQUIRES = string xpcom
|
||||
|
||||
CSRCS += \
|
||||
bloatblame.c \
|
||||
leakstats.c \
|
||||
$(NULL)
|
||||
|
||||
SIMPLE_PROGRAMS = $(CSRCS:.c=$(BIN_SUFFIX))
|
||||
|
||||
include $(topsrcdir)/config/config.mk
|
||||
|
||||
LIBS += \
|
||||
$(NSPR_LIBS) \
|
||||
tmreader.o \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_DEPS = tmreader.o
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,167 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is nsTraceMalloc.c/bloatblame.c code, released
|
||||
* April 19, 2000.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Brendan Eich, 14-April-2000
|
||||
* L. David Baron, 2001-06-07, created leakstats.c based on bloatblame.c
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the
|
||||
* terms of the GNU Public License (the "GPL"), in which case the
|
||||
* provisions of the GPL are applicable instead of those above.
|
||||
* If you wish to allow use of your version of this file only
|
||||
* under the terms of the GPL and not to allow others to use your
|
||||
* version of this file under the MPL, indicate your decision by
|
||||
* deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this
|
||||
* file under either the MPL or the GPL.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_GETOPT_H
|
||||
#include <getopt.h>
|
||||
#else
|
||||
extern int getopt(int argc, char *const *argv, const char *shortopts);
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include "nsTraceMalloc.h"
|
||||
#include "tmreader.h"
|
||||
|
||||
static char *program;
|
||||
|
||||
typedef struct handler_data {
|
||||
uint32 current_heapsize;
|
||||
uint32 max_heapsize;
|
||||
uint32 bytes_allocated;
|
||||
uint32 current_allocations;
|
||||
uint32 total_allocations;
|
||||
int finished;
|
||||
} handler_data;
|
||||
|
||||
static void handler_data_init(handler_data *data)
|
||||
{
|
||||
data->current_heapsize = 0;
|
||||
data->max_heapsize = 0;
|
||||
data->bytes_allocated = 0;
|
||||
data->current_allocations = 0;
|
||||
data->total_allocations = 0;
|
||||
data->finished = 0;
|
||||
}
|
||||
|
||||
static void handler_data_finish(handler_data *data)
|
||||
{
|
||||
}
|
||||
|
||||
static void my_tmevent_handler(tmreader *tmr, tmevent *event)
|
||||
{
|
||||
handler_data *data = (handler_data*) tmr->data;
|
||||
|
||||
switch (event->type) {
|
||||
case TM_EVENT_REALLOC:
|
||||
data->current_heapsize -= event->u.alloc.oldsize;
|
||||
--data->current_allocations;
|
||||
/* fall-through intentional */
|
||||
case TM_EVENT_MALLOC:
|
||||
case TM_EVENT_CALLOC:
|
||||
++data->current_allocations;
|
||||
++data->total_allocations;
|
||||
data->bytes_allocated += event->u.alloc.size;
|
||||
data->current_heapsize += event->u.alloc.size;
|
||||
if (data->current_heapsize > data->max_heapsize)
|
||||
data->max_heapsize = data->current_heapsize;
|
||||
break;
|
||||
case TM_EVENT_FREE:
|
||||
--data->current_allocations;
|
||||
data->current_heapsize -= event->u.alloc.size;
|
||||
break;
|
||||
case TM_EVENT_STATS:
|
||||
data->finished = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, i, j, rv;
|
||||
tmreader *tmr;
|
||||
FILE *fp;
|
||||
time_t start;
|
||||
handler_data data;
|
||||
|
||||
program = *argv;
|
||||
|
||||
handler_data_init(&data);
|
||||
tmr = tmreader_new(program, &data);
|
||||
if (!tmr) {
|
||||
perror(program);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
start = time(NULL);
|
||||
fprintf(stdout, "%s starting at %s", program, ctime(&start));
|
||||
fflush(stdout);
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc == 0) {
|
||||
if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0)
|
||||
exit(1);
|
||||
} else {
|
||||
for (i = j = 0; i < argc; i++) {
|
||||
fp = fopen(argv[i], "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "%s: can't open %s: %s\n",
|
||||
program, argv[i], strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler);
|
||||
if (rv < 0)
|
||||
exit(1);
|
||||
if (rv > 0)
|
||||
j++;
|
||||
fclose(fp);
|
||||
}
|
||||
if (j == 0)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!data.finished) {
|
||||
fprintf(stderr, "%s: log file incomplete\n", program);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stdout,
|
||||
"Leaks: %u bytes, %u allocations\n"
|
||||
"Maximum Heap Size: %u bytes\n"
|
||||
"%u bytes were allocated in %u allocations.\n",
|
||||
data.current_heapsize, data.current_allocations,
|
||||
data.max_heapsize,
|
||||
data.bytes_allocated, data.total_allocations);
|
||||
|
||||
handler_data_finish(&data);
|
||||
tmreader_destroy(tmr);
|
||||
|
||||
exit(0);
|
||||
}
|
|
@ -56,6 +56,7 @@
|
|||
#include "prlog.h"
|
||||
#include "prmon.h"
|
||||
#include "prprf.h"
|
||||
#include "prenv.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
|
||||
#ifdef XP_WIN32
|
||||
|
@ -68,7 +69,7 @@
|
|||
|
||||
#define WRITE_FLAGS "w"
|
||||
|
||||
#endif //WIN32
|
||||
#endif /* WIN32 */
|
||||
|
||||
|
||||
#ifdef XP_UNIX
|
||||
|
@ -268,6 +269,7 @@ static logfile *logfile_list = NULL;
|
|||
static logfile **logfile_tail = &logfile_list;
|
||||
static logfile *logfp = &default_logfile;
|
||||
static PRMonitor *tmmon = NULL;
|
||||
static char *sdlogname = NULL; /* filename for shutdown leak log */
|
||||
|
||||
/* We don't want more than 32 logfiles open at once, ok? */
|
||||
typedef uint32 lfd_set;
|
||||
|
@ -318,7 +320,7 @@ retry:
|
|||
static void flush_logfile(logfile *fp)
|
||||
{
|
||||
int len, cnt;
|
||||
int fd;
|
||||
int fd;
|
||||
char *bp;
|
||||
|
||||
len = fp->pos;
|
||||
|
@ -570,15 +572,17 @@ static callsite *calltree(int skip)
|
|||
if (! ok)
|
||||
return 0;
|
||||
|
||||
// Get the context information for this thread. That way we will
|
||||
// know where our sp, fp, pc, etc. are and can fill in the
|
||||
// STACKFRAME with the initial values.
|
||||
/*
|
||||
* Get the context information for this thread. That way we will
|
||||
* know where our sp, fp, pc, etc. are and can fill in the
|
||||
* STACKFRAME with the initial values.
|
||||
*/
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
ok = GetThreadContext(myThread, &context);
|
||||
if (! ok)
|
||||
return 0;
|
||||
|
||||
// Setup initial stack frame to walk from
|
||||
/* Setup initial stack frame to walk from */
|
||||
memset(&(frame[0]), 0, sizeof(frame[0]));
|
||||
frame[0].AddrPC.Offset = context.Eip;
|
||||
frame[0].AddrPC.Mode = AddrModeFlat;
|
||||
|
@ -598,10 +602,11 @@ static callsite *calltree(int skip)
|
|||
myThread,
|
||||
&(frame[framenum]),
|
||||
&context,
|
||||
0, // read process memory routine
|
||||
_SymFunctionTableAccess, // function table access routine
|
||||
_SymGetModuleBase, // module base routine
|
||||
0); // translate address routine
|
||||
0, /* read process memory routine */
|
||||
_SymFunctionTableAccess, /* function table access
|
||||
routine */
|
||||
_SymGetModuleBase, /* module base routine */
|
||||
0); /* translate address routine */
|
||||
|
||||
if (!ok) {
|
||||
break;
|
||||
|
@ -676,7 +681,7 @@ static callsite *calltree(int skip)
|
|||
{
|
||||
DWORD error = GetLastError();
|
||||
PR_ASSERT(error);
|
||||
library = "unknown";//ew
|
||||
library = "unknown";/* ew */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -707,7 +712,7 @@ static callsite *calltree(int skip)
|
|||
hash = PL_HashString(library);
|
||||
hep = PL_HashTableRawLookup(libraries, hash, library);
|
||||
he = *hep;
|
||||
library = strdup(library); //strdup it always?
|
||||
library = strdup(library); /* strdup it always? */
|
||||
if (he) {
|
||||
library_serial = (uint32) he->value;
|
||||
le = (lfdset_entry *) he;
|
||||
|
@ -716,7 +721,7 @@ static callsite *calltree(int skip)
|
|||
le = NULL;
|
||||
}
|
||||
} else {
|
||||
// library = strdup(library);
|
||||
/* library = strdup(library); */
|
||||
if (library) {
|
||||
library_serial = ++library_serial_generator;
|
||||
he = PL_HashTableRawAdd(libraries, hep, hash, library,
|
||||
|
@ -1430,97 +1435,131 @@ PR_IMPLEMENT(void) NS_TraceMallocStartup(int logfd)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Options for log files, with the log file name either as the next option
|
||||
* or separated by '=' (e.g. "./mozilla --trace-malloc * malloc.log" or
|
||||
* "./mozilla --trace-malloc=malloc.log").
|
||||
*/
|
||||
static const char TMLOG_OPTION[] = "--trace-malloc";
|
||||
static const char SDLOG_OPTION[] = "--shutdown-leaks";
|
||||
|
||||
#define SHOULD_PARSE_ARG(name_, log_, arg_) \
|
||||
(0 == strncmp(arg_, name_, sizeof(name_) - 1))
|
||||
|
||||
#define PARSE_ARG(name_, log_, argv_, i_, consumed_) \
|
||||
PR_BEGIN_MACRO \
|
||||
char _nextchar = argv_[i_][sizeof(name_) - 1]; \
|
||||
if (_nextchar == '=') { \
|
||||
log_ = argv_[i_] + sizeof(name_); \
|
||||
consumed_ = 1; \
|
||||
} else if (_nextchar == '\0') { \
|
||||
log_ = argv_[i_+1]; \
|
||||
consumed_ = 2; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
PR_IMPLEMENT(int) NS_TraceMallocStartupArgs(int argc, char* argv[])
|
||||
{
|
||||
int i, logfd = -1;
|
||||
int i, logfd = -1, consumed;
|
||||
char *tmlogname = NULL; /* note global |sdlogname| */
|
||||
|
||||
/*
|
||||
* Look for the --trace-malloc <logfile> option early, to avoid missing
|
||||
* early mallocs (we miss static constructors whose output overflows the
|
||||
* log file's static 16K output buffer).
|
||||
*/
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--trace-malloc") == 0 && i < argc-1) {
|
||||
char *logfilename;
|
||||
int pipefds[2];
|
||||
for (i = 1; i < argc; i += consumed) {
|
||||
consumed = 0;
|
||||
if (SHOULD_PARSE_ARG(TMLOG_OPTION, tmlogname, argv[i]))
|
||||
PARSE_ARG(TMLOG_OPTION, tmlogname, argv, i, consumed);
|
||||
else if (SHOULD_PARSE_ARG(SDLOG_OPTION, sdlogname, argv[i]))
|
||||
PARSE_ARG(SDLOG_OPTION, sdlogname, argv, i, consumed);
|
||||
|
||||
logfilename = argv[i+1];
|
||||
switch (*logfilename) {
|
||||
#if XP_UNIX
|
||||
case '|':
|
||||
if (pipe(pipefds) == 0) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* In child: set up stdin, parse args, and exec. */
|
||||
int maxargc, nargc;
|
||||
char **nargv, *token;
|
||||
|
||||
if (pipefds[0] != 0) {
|
||||
dup2(pipefds[0], 0);
|
||||
close(pipefds[0]);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
||||
logfilename = strtok(logfilename + 1, " \t");
|
||||
maxargc = 3;
|
||||
nargv = (char **) malloc((maxargc+1) * sizeof(char *));
|
||||
if (!nargv) exit(1);
|
||||
nargc = 0;
|
||||
nargv[nargc++] = logfilename;
|
||||
while ((token = strtok(NULL, " \t")) != NULL) {
|
||||
if (nargc == maxargc) {
|
||||
maxargc *= 2;
|
||||
nargv = (char**)
|
||||
realloc(nargv, (maxargc+1) * sizeof(char*));
|
||||
if (!nargv) exit(1);
|
||||
}
|
||||
nargv[nargc++] = token;
|
||||
}
|
||||
nargv[nargc] = NULL;
|
||||
|
||||
(void) setsid();
|
||||
execvp(logfilename, nargv);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
/* In parent: set logfd to the pipe's write side. */
|
||||
close(pipefds[0]);
|
||||
logfd = pipefds[1];
|
||||
}
|
||||
}
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't pipe to trace-malloc child process %s: %s\n",
|
||||
argv[0], logfilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
#endif /*XP_UNIX*/
|
||||
case '-':
|
||||
/* Don't log from startup, but do prepare to log later. */
|
||||
/* XXX traditional meaning of '-' as option argument is "stdin" or "stdout" */
|
||||
if (logfilename[1] == '\0')
|
||||
break;
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
logfd = open(logfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't create trace-malloc logfilename %s: %s\n",
|
||||
argv[0], logfilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifndef XP_WIN32
|
||||
if (consumed) {
|
||||
#ifndef XP_WIN32 /* If we don't comment this out, it will crash Windows. */
|
||||
int j;
|
||||
/* Now remove --trace-malloc and its argument from argv. */
|
||||
for (argc -= 2; i < argc; i++)
|
||||
argv[i] = argv[i+2];
|
||||
argc -= consumed;
|
||||
for (j = i; j < argc; ++j)
|
||||
argv[j] = argv[j+consumed];
|
||||
argv[argc] = NULL;
|
||||
#endif//if you dont comment this out it will crash windows
|
||||
consumed = 0; /* don't advance next iteration */
|
||||
#endif
|
||||
} else {
|
||||
consumed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmlogname) {
|
||||
int pipefds[2];
|
||||
|
||||
switch (*tmlogname) {
|
||||
#if XP_UNIX
|
||||
case '|':
|
||||
if (pipe(pipefds) == 0) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* In child: set up stdin, parse args, and exec. */
|
||||
int maxargc, nargc;
|
||||
char **nargv, *token;
|
||||
|
||||
if (pipefds[0] != 0) {
|
||||
dup2(pipefds[0], 0);
|
||||
close(pipefds[0]);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
||||
tmlogname = strtok(tmlogname + 1, " \t");
|
||||
maxargc = 3;
|
||||
nargv = (char **) malloc((maxargc+1) * sizeof(char *));
|
||||
if (!nargv) exit(1);
|
||||
nargc = 0;
|
||||
nargv[nargc++] = tmlogname;
|
||||
while ((token = strtok(NULL, " \t")) != NULL) {
|
||||
if (nargc == maxargc) {
|
||||
maxargc *= 2;
|
||||
nargv = (char**)
|
||||
realloc(nargv, (maxargc+1) * sizeof(char*));
|
||||
if (!nargv) exit(1);
|
||||
}
|
||||
nargv[nargc++] = token;
|
||||
}
|
||||
nargv[nargc] = NULL;
|
||||
|
||||
(void) setsid();
|
||||
execvp(tmlogname, nargv);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
/* In parent: set logfd to the pipe's write side. */
|
||||
close(pipefds[0]);
|
||||
logfd = pipefds[1];
|
||||
}
|
||||
}
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't pipe to trace-malloc child process %s: %s\n",
|
||||
argv[0], tmlogname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
#endif /*XP_UNIX*/
|
||||
case '-':
|
||||
/* Don't log from startup, but do prepare to log later. */
|
||||
/* XXX traditional meaning of '-' as option argument is "stdin" or "stdout" */
|
||||
if (tmlogname[1] == '\0')
|
||||
break;
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
logfd = open(tmlogname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't create trace-malloc log named %s: %s\n",
|
||||
argv[0], tmlogname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1533,6 +1572,9 @@ PR_IMPLEMENT(void) NS_TraceMallocShutdown()
|
|||
{
|
||||
logfile *fp;
|
||||
|
||||
if (sdlogname)
|
||||
NS_TraceMallocDumpAllocations(sdlogname);
|
||||
|
||||
if (tmstats.backtrace_failures) {
|
||||
fprintf(stderr,
|
||||
"TraceMalloc backtrace failures: %lu (malloc %lu dladdr %lu)\n",
|
||||
|
@ -1683,7 +1725,7 @@ NS_TraceMallocLogTimestamp(const char *caption)
|
|||
#endif
|
||||
#if defined(XP_WIN32)
|
||||
struct _timeb tb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
if (tmmon)
|
||||
|
|
|
@ -81,10 +81,6 @@ CSRCS += nsTraceMalloc.c
|
|||
CPPSRCS += nsTypeInfo.cpp
|
||||
EXPORTS += nsTraceMalloc.h
|
||||
DEFINES += -DNS_TRACE_MALLOC
|
||||
SIMPLE_PROGRAMS = bloatblame
|
||||
|
||||
LIBS += tmreader.o $(NSPR_LIBS)
|
||||
EXTRA_DEPS = tmreader.o
|
||||
endif
|
||||
|
||||
XPIDLSRCS = \
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
#include "prlog.h"
|
||||
#include "prmon.h"
|
||||
#include "prprf.h"
|
||||
#include "prenv.h"
|
||||
#include "nsTraceMalloc.h"
|
||||
|
||||
#ifdef XP_WIN32
|
||||
|
@ -68,7 +69,7 @@
|
|||
|
||||
#define WRITE_FLAGS "w"
|
||||
|
||||
#endif //WIN32
|
||||
#endif /* WIN32 */
|
||||
|
||||
|
||||
#ifdef XP_UNIX
|
||||
|
@ -268,6 +269,7 @@ static logfile *logfile_list = NULL;
|
|||
static logfile **logfile_tail = &logfile_list;
|
||||
static logfile *logfp = &default_logfile;
|
||||
static PRMonitor *tmmon = NULL;
|
||||
static char *sdlogname = NULL; /* filename for shutdown leak log */
|
||||
|
||||
/* We don't want more than 32 logfiles open at once, ok? */
|
||||
typedef uint32 lfd_set;
|
||||
|
@ -318,7 +320,7 @@ retry:
|
|||
static void flush_logfile(logfile *fp)
|
||||
{
|
||||
int len, cnt;
|
||||
int fd;
|
||||
int fd;
|
||||
char *bp;
|
||||
|
||||
len = fp->pos;
|
||||
|
@ -570,15 +572,17 @@ static callsite *calltree(int skip)
|
|||
if (! ok)
|
||||
return 0;
|
||||
|
||||
// Get the context information for this thread. That way we will
|
||||
// know where our sp, fp, pc, etc. are and can fill in the
|
||||
// STACKFRAME with the initial values.
|
||||
/*
|
||||
* Get the context information for this thread. That way we will
|
||||
* know where our sp, fp, pc, etc. are and can fill in the
|
||||
* STACKFRAME with the initial values.
|
||||
*/
|
||||
context.ContextFlags = CONTEXT_FULL;
|
||||
ok = GetThreadContext(myThread, &context);
|
||||
if (! ok)
|
||||
return 0;
|
||||
|
||||
// Setup initial stack frame to walk from
|
||||
/* Setup initial stack frame to walk from */
|
||||
memset(&(frame[0]), 0, sizeof(frame[0]));
|
||||
frame[0].AddrPC.Offset = context.Eip;
|
||||
frame[0].AddrPC.Mode = AddrModeFlat;
|
||||
|
@ -598,10 +602,11 @@ static callsite *calltree(int skip)
|
|||
myThread,
|
||||
&(frame[framenum]),
|
||||
&context,
|
||||
0, // read process memory routine
|
||||
_SymFunctionTableAccess, // function table access routine
|
||||
_SymGetModuleBase, // module base routine
|
||||
0); // translate address routine
|
||||
0, /* read process memory routine */
|
||||
_SymFunctionTableAccess, /* function table access
|
||||
routine */
|
||||
_SymGetModuleBase, /* module base routine */
|
||||
0); /* translate address routine */
|
||||
|
||||
if (!ok) {
|
||||
break;
|
||||
|
@ -676,7 +681,7 @@ static callsite *calltree(int skip)
|
|||
{
|
||||
DWORD error = GetLastError();
|
||||
PR_ASSERT(error);
|
||||
library = "unknown";//ew
|
||||
library = "unknown";/* ew */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -707,7 +712,7 @@ static callsite *calltree(int skip)
|
|||
hash = PL_HashString(library);
|
||||
hep = PL_HashTableRawLookup(libraries, hash, library);
|
||||
he = *hep;
|
||||
library = strdup(library); //strdup it always?
|
||||
library = strdup(library); /* strdup it always? */
|
||||
if (he) {
|
||||
library_serial = (uint32) he->value;
|
||||
le = (lfdset_entry *) he;
|
||||
|
@ -716,7 +721,7 @@ static callsite *calltree(int skip)
|
|||
le = NULL;
|
||||
}
|
||||
} else {
|
||||
// library = strdup(library);
|
||||
/* library = strdup(library); */
|
||||
if (library) {
|
||||
library_serial = ++library_serial_generator;
|
||||
he = PL_HashTableRawAdd(libraries, hep, hash, library,
|
||||
|
@ -1430,97 +1435,131 @@ PR_IMPLEMENT(void) NS_TraceMallocStartup(int logfd)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* Options for log files, with the log file name either as the next option
|
||||
* or separated by '=' (e.g. "./mozilla --trace-malloc * malloc.log" or
|
||||
* "./mozilla --trace-malloc=malloc.log").
|
||||
*/
|
||||
static const char TMLOG_OPTION[] = "--trace-malloc";
|
||||
static const char SDLOG_OPTION[] = "--shutdown-leaks";
|
||||
|
||||
#define SHOULD_PARSE_ARG(name_, log_, arg_) \
|
||||
(0 == strncmp(arg_, name_, sizeof(name_) - 1))
|
||||
|
||||
#define PARSE_ARG(name_, log_, argv_, i_, consumed_) \
|
||||
PR_BEGIN_MACRO \
|
||||
char _nextchar = argv_[i_][sizeof(name_) - 1]; \
|
||||
if (_nextchar == '=') { \
|
||||
log_ = argv_[i_] + sizeof(name_); \
|
||||
consumed_ = 1; \
|
||||
} else if (_nextchar == '\0') { \
|
||||
log_ = argv_[i_+1]; \
|
||||
consumed_ = 2; \
|
||||
} \
|
||||
PR_END_MACRO
|
||||
|
||||
PR_IMPLEMENT(int) NS_TraceMallocStartupArgs(int argc, char* argv[])
|
||||
{
|
||||
int i, logfd = -1;
|
||||
int i, logfd = -1, consumed;
|
||||
char *tmlogname = NULL; /* note global |sdlogname| */
|
||||
|
||||
/*
|
||||
* Look for the --trace-malloc <logfile> option early, to avoid missing
|
||||
* early mallocs (we miss static constructors whose output overflows the
|
||||
* log file's static 16K output buffer).
|
||||
*/
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--trace-malloc") == 0 && i < argc-1) {
|
||||
char *logfilename;
|
||||
int pipefds[2];
|
||||
for (i = 1; i < argc; i += consumed) {
|
||||
consumed = 0;
|
||||
if (SHOULD_PARSE_ARG(TMLOG_OPTION, tmlogname, argv[i]))
|
||||
PARSE_ARG(TMLOG_OPTION, tmlogname, argv, i, consumed);
|
||||
else if (SHOULD_PARSE_ARG(SDLOG_OPTION, sdlogname, argv[i]))
|
||||
PARSE_ARG(SDLOG_OPTION, sdlogname, argv, i, consumed);
|
||||
|
||||
logfilename = argv[i+1];
|
||||
switch (*logfilename) {
|
||||
#if XP_UNIX
|
||||
case '|':
|
||||
if (pipe(pipefds) == 0) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* In child: set up stdin, parse args, and exec. */
|
||||
int maxargc, nargc;
|
||||
char **nargv, *token;
|
||||
|
||||
if (pipefds[0] != 0) {
|
||||
dup2(pipefds[0], 0);
|
||||
close(pipefds[0]);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
||||
logfilename = strtok(logfilename + 1, " \t");
|
||||
maxargc = 3;
|
||||
nargv = (char **) malloc((maxargc+1) * sizeof(char *));
|
||||
if (!nargv) exit(1);
|
||||
nargc = 0;
|
||||
nargv[nargc++] = logfilename;
|
||||
while ((token = strtok(NULL, " \t")) != NULL) {
|
||||
if (nargc == maxargc) {
|
||||
maxargc *= 2;
|
||||
nargv = (char**)
|
||||
realloc(nargv, (maxargc+1) * sizeof(char*));
|
||||
if (!nargv) exit(1);
|
||||
}
|
||||
nargv[nargc++] = token;
|
||||
}
|
||||
nargv[nargc] = NULL;
|
||||
|
||||
(void) setsid();
|
||||
execvp(logfilename, nargv);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
/* In parent: set logfd to the pipe's write side. */
|
||||
close(pipefds[0]);
|
||||
logfd = pipefds[1];
|
||||
}
|
||||
}
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't pipe to trace-malloc child process %s: %s\n",
|
||||
argv[0], logfilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
#endif /*XP_UNIX*/
|
||||
case '-':
|
||||
/* Don't log from startup, but do prepare to log later. */
|
||||
/* XXX traditional meaning of '-' as option argument is "stdin" or "stdout" */
|
||||
if (logfilename[1] == '\0')
|
||||
break;
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
logfd = open(logfilename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't create trace-malloc logfilename %s: %s\n",
|
||||
argv[0], logfilename, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifndef XP_WIN32
|
||||
if (consumed) {
|
||||
#ifndef XP_WIN32 /* If we don't comment this out, it will crash Windows. */
|
||||
int j;
|
||||
/* Now remove --trace-malloc and its argument from argv. */
|
||||
for (argc -= 2; i < argc; i++)
|
||||
argv[i] = argv[i+2];
|
||||
argc -= consumed;
|
||||
for (j = i; j < argc; ++j)
|
||||
argv[j] = argv[j+consumed];
|
||||
argv[argc] = NULL;
|
||||
#endif//if you dont comment this out it will crash windows
|
||||
consumed = 0; /* don't advance next iteration */
|
||||
#endif
|
||||
} else {
|
||||
consumed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (tmlogname) {
|
||||
int pipefds[2];
|
||||
|
||||
switch (*tmlogname) {
|
||||
#if XP_UNIX
|
||||
case '|':
|
||||
if (pipe(pipefds) == 0) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* In child: set up stdin, parse args, and exec. */
|
||||
int maxargc, nargc;
|
||||
char **nargv, *token;
|
||||
|
||||
if (pipefds[0] != 0) {
|
||||
dup2(pipefds[0], 0);
|
||||
close(pipefds[0]);
|
||||
}
|
||||
close(pipefds[1]);
|
||||
|
||||
tmlogname = strtok(tmlogname + 1, " \t");
|
||||
maxargc = 3;
|
||||
nargv = (char **) malloc((maxargc+1) * sizeof(char *));
|
||||
if (!nargv) exit(1);
|
||||
nargc = 0;
|
||||
nargv[nargc++] = tmlogname;
|
||||
while ((token = strtok(NULL, " \t")) != NULL) {
|
||||
if (nargc == maxargc) {
|
||||
maxargc *= 2;
|
||||
nargv = (char**)
|
||||
realloc(nargv, (maxargc+1) * sizeof(char*));
|
||||
if (!nargv) exit(1);
|
||||
}
|
||||
nargv[nargc++] = token;
|
||||
}
|
||||
nargv[nargc] = NULL;
|
||||
|
||||
(void) setsid();
|
||||
execvp(tmlogname, nargv);
|
||||
exit(127);
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
/* In parent: set logfd to the pipe's write side. */
|
||||
close(pipefds[0]);
|
||||
logfd = pipefds[1];
|
||||
}
|
||||
}
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't pipe to trace-malloc child process %s: %s\n",
|
||||
argv[0], tmlogname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
#endif /*XP_UNIX*/
|
||||
case '-':
|
||||
/* Don't log from startup, but do prepare to log later. */
|
||||
/* XXX traditional meaning of '-' as option argument is "stdin" or "stdout" */
|
||||
if (tmlogname[1] == '\0')
|
||||
break;
|
||||
/* FALL THROUGH */
|
||||
|
||||
default:
|
||||
logfd = open(tmlogname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
||||
if (logfd < 0) {
|
||||
fprintf(stderr,
|
||||
"%s: can't create trace-malloc log named %s: %s\n",
|
||||
argv[0], tmlogname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1533,6 +1572,9 @@ PR_IMPLEMENT(void) NS_TraceMallocShutdown()
|
|||
{
|
||||
logfile *fp;
|
||||
|
||||
if (sdlogname)
|
||||
NS_TraceMallocDumpAllocations(sdlogname);
|
||||
|
||||
if (tmstats.backtrace_failures) {
|
||||
fprintf(stderr,
|
||||
"TraceMalloc backtrace failures: %lu (malloc %lu dladdr %lu)\n",
|
||||
|
@ -1683,7 +1725,7 @@ NS_TraceMallocLogTimestamp(const char *caption)
|
|||
#endif
|
||||
#if defined(XP_WIN32)
|
||||
struct _timeb tb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
if (tmmon)
|
||||
|
|
Загрузка…
Ссылка в новой задаче