michael@0: /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #ifdef HAVE_GETOPT_H michael@0: #include michael@0: #else michael@0: extern int getopt(int argc, char *const *argv, const char *shortopts); michael@0: extern char *optarg; michael@0: extern int optind; michael@0: #ifdef XP_WIN32 michael@0: int optind=1; michael@0: #endif michael@0: #endif michael@0: #include michael@0: #include "nsTraceMalloc.h" michael@0: #include "tmreader.h" michael@0: #include "prlog.h" michael@0: michael@0: static char *program; michael@0: michael@0: typedef struct handler_data { michael@0: uint32_t current_heapsize; michael@0: uint32_t max_heapsize; michael@0: uint32_t bytes_allocated; michael@0: uint32_t current_allocations; michael@0: uint32_t total_allocations; michael@0: uint32_t unmatched_frees; michael@0: int finished; michael@0: } handler_data; michael@0: michael@0: static void handler_data_init(handler_data *data) michael@0: { michael@0: data->current_heapsize = 0; michael@0: data->max_heapsize = 0; michael@0: data->bytes_allocated = 0; michael@0: data->current_allocations = 0; michael@0: data->total_allocations = 0; michael@0: data->unmatched_frees = 0; michael@0: data->finished = 0; michael@0: } michael@0: michael@0: static void handler_data_finish(handler_data *data) michael@0: { michael@0: } michael@0: michael@0: static void my_tmevent_handler(tmreader *tmr, tmevent *event) michael@0: { michael@0: handler_data *data = (handler_data*) tmr->data; michael@0: michael@0: switch (event->type) { michael@0: case TM_EVENT_REALLOC: michael@0: /* On Windows, original allocation could be before we overrode malloc */ michael@0: if (event->u.alloc.oldserial != 0) { michael@0: data->current_heapsize -= event->u.alloc.oldsize; michael@0: --data->current_allocations; michael@0: } else { michael@0: ++data->unmatched_frees; michael@0: PR_ASSERT(event->u.alloc.oldsize == 0); michael@0: } michael@0: /* fall-through intentional */ michael@0: case TM_EVENT_MALLOC: michael@0: case TM_EVENT_CALLOC: michael@0: ++data->current_allocations; michael@0: ++data->total_allocations; michael@0: data->bytes_allocated += event->u.alloc.size; michael@0: data->current_heapsize += event->u.alloc.size; michael@0: if (data->current_heapsize > data->max_heapsize) michael@0: data->max_heapsize = data->current_heapsize; michael@0: break; michael@0: case TM_EVENT_FREE: michael@0: /* On Windows, original allocation could be before we overrode malloc */ michael@0: if (event->serial != 0) { michael@0: --data->current_allocations; michael@0: data->current_heapsize -= event->u.alloc.size; michael@0: } else { michael@0: ++data->unmatched_frees; michael@0: PR_ASSERT(event->u.alloc.size == 0); michael@0: } michael@0: break; michael@0: case TM_EVENT_STATS: michael@0: data->finished = 1; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: michael@0: int main(int argc, char **argv) michael@0: { michael@0: int i, j, rv; michael@0: tmreader *tmr; michael@0: FILE *fp; michael@0: time_t start; michael@0: handler_data data; michael@0: michael@0: program = *argv; michael@0: michael@0: handler_data_init(&data); michael@0: tmr = tmreader_new(program, &data); michael@0: if (!tmr) { michael@0: perror(program); michael@0: exit(1); michael@0: } michael@0: michael@0: start = time(NULL); michael@0: fprintf(stdout, "%s starting at %s", program, ctime(&start)); michael@0: fflush(stdout); michael@0: michael@0: argc -= optind; michael@0: argv += optind; michael@0: if (argc == 0) { michael@0: if (tmreader_eventloop(tmr, "-", my_tmevent_handler) <= 0) michael@0: exit(1); michael@0: } else { michael@0: for (i = j = 0; i < argc; i++) { michael@0: fp = fopen(argv[i], "r"); michael@0: if (!fp) { michael@0: fprintf(stderr, michael@0: "TEST-UNEXPECTED-FAIL | leakstats | can't open %s: %s\n", michael@0: argv[i], strerror(errno)); michael@0: exit(1); michael@0: } michael@0: rv = tmreader_eventloop(tmr, argv[i], my_tmevent_handler); michael@0: if (rv < 0) michael@0: exit(1); michael@0: if (rv > 0) michael@0: j++; michael@0: fclose(fp); michael@0: } michael@0: if (j == 0) michael@0: exit(1); michael@0: } michael@0: michael@0: if (!data.finished) { michael@0: fprintf(stderr, "TEST-UNEXPECTED-FAIL | leakstats | log file incomplete\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: fprintf(stdout, michael@0: "Leaks: %u bytes, %u allocations\n" michael@0: "Maximum Heap Size: %u bytes\n" michael@0: "%u bytes were allocated in %u allocations.\n", michael@0: data.current_heapsize, data.current_allocations, michael@0: data.max_heapsize, michael@0: data.bytes_allocated, data.total_allocations); michael@0: if (data.unmatched_frees != 0) michael@0: fprintf(stdout, michael@0: "Logged %u free (or realloc) calls for which we missed the " michael@0: "original malloc.\n", michael@0: data.unmatched_frees); michael@0: michael@0: handler_data_finish(&data); michael@0: tmreader_destroy(tmr); michael@0: michael@0: exit(0); michael@0: }