From 802a1a3b223d7c11ac2831ef2e918c3e65ba0881 Mon Sep 17 00:00:00 2001 From: Stefani Seibold Date: Wed, 28 Oct 2015 07:57:53 +0100 Subject: [PATCH] introduce new features - disable hardware breakpoints useful for running mtrace inside a virtual machine - sanity check check new/new[] against mismatch operation and all c allocation functions against c++ allocation functions and visa versa. --- breakpoint.c | 3 ++ client/client.c | 4 ++ client/process.c | 117 ++++++++++++++++++++++++++++++++++++++++------ client/process.h | 1 + client/readline.c | 1 + memtrace.h | 4 ++ mtrace.1 | 22 +++++++-- options.c | 37 +++++++++++++-- options.h | 3 ++ report.c | 96 ++++++++++++++++++++++++------------- 10 files changed, 236 insertions(+), 52 deletions(-) diff --git a/breakpoint.c b/breakpoint.c index 29d9af2..14643fe 100644 --- a/breakpoint.c +++ b/breakpoint.c @@ -329,6 +329,9 @@ struct breakpoint *breakpoint_new_ext(struct task *task, arch_addr_t addr, struc if (bp == NULL) goto fail1; + if (options.nohwbp) + bp_type = BP_SW; + bp->on_hit = NULL; bp->libsym = libsym; bp->addr = addr; diff --git a/client/client.c b/client/client.c index db15c68..727851b 100644 --- a/client/client.c +++ b/client/client.c @@ -427,10 +427,14 @@ static int client_func(void) case MT_PVALLOC: case MT_MMAP: case MT_MMAP64: + case MT_NEW: + case MT_NEW_ARRAY: process_alloc(process, &mt_msg, payload); break; case MT_REALLOC_ENTER: case MT_FREE: + case MT_DELETE: + case MT_DELETE_ARRAY: process_free(process, &mt_msg, payload); break; case MT_MUNMAP: diff --git a/client/process.c b/client/process.c index 023f645..013ae1b 100644 --- a/client/process.c +++ b/client/process.c @@ -70,6 +70,7 @@ struct rb_stack { unsigned long long bytes_used; unsigned long long bytes_leaked; unsigned long long tsc; + unsigned long long n_mismatched; }; struct map { @@ -139,6 +140,14 @@ static const char *str_operation(enum mt_operation operation) return "free"; case MT_MUNMAP: return "munmap"; + case MT_NEW: + return "new"; + case MT_NEW_ARRAY: + return "new[]"; + case MT_DELETE: + return "delete"; + case MT_DELETE_ARRAY: + return "delete[]"; default: break; } @@ -422,6 +431,7 @@ static struct rb_stack *stack_clone(struct process *process, struct rb_stack *st this->leaks = stack_node->leaks; this->n_allocations = stack_node->n_allocations; + this->n_mismatched = stack_node->n_mismatched; this->total_allocations = stack_node->total_allocations; this->bytes_used = stack_node->bytes_used; this->bytes_leaked = stack_node->bytes_leaked; @@ -486,6 +496,7 @@ static struct rb_stack *stack_add(struct process *process, pid_t pid, void *addr memcpy(stack->addrs, addrs, stack_size); this->n_allocations = 0; + this->n_mismatched = 0; this->total_allocations = 0; this->bytes_used = 0; this->leaks = 0; @@ -832,6 +843,15 @@ static int sort_tsc(const struct rb_stack **p, const struct rb_stack **q) return 0; } +static int sort_mismatched(const struct rb_stack **p, const struct rb_stack **q) +{ + if ((*p)->n_mismatched > (*q)->n_mismatched) + return -1; + if ((*p)->n_mismatched < (*q)->n_mismatched) + return 1; + return 0; +} + static int sort_usage(const struct rb_stack **p, const struct rb_stack **q) { if ((*p)->bytes_used > (*q)->bytes_used) @@ -939,20 +959,31 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb struct rb_stack *stack = arr[i]; if (!skipfunc(stack)) { - if (dump_printf( - "Stack (%s):\n" - " bytes used: %llu\n" - " number of open allocations: %llu\n" - " total number of allocations: %llu\n", - str_operation(stack->stack->operation), - stack->bytes_used, - stack->n_allocations, - stack->total_allocations - ) == -1) - break; + if (!stack->n_mismatched) { + if (dump_printf( + "Stack (%s):\n" + " bytes used: %llu\n" + " number of open allocations: %llu\n" + " total number of allocations: %llu\n", + str_operation(stack->stack->operation), + stack->bytes_used, + stack->n_allocations, + stack->total_allocations + ) == -1) + break; - if (stack->leaks) { - if (dump_printf( " leaked allocations: %lu (%llu bytes)\n", stack->leaks, stack->bytes_leaked) == -1) + if (stack->leaks) { + if (dump_printf( " leaked allocations: %lu (%llu bytes)\n", stack->leaks, stack->bytes_leaked) == -1) + break; + } + } + else { + if (dump_printf( + "Stack (%s):\n" + " total number of mismatches: %llu\n", + str_operation(stack->stack->operation), + stack->n_mismatched + ) == -1) break; } @@ -995,6 +1026,11 @@ static int skip_zero_allocations(struct rb_stack *stack) return !stack->n_allocations; } +static int skip_non_mismatched(struct rb_stack *stack) +{ + return !stack->n_mismatched; +} + static int skip_zero_leaks(struct rb_stack *stack) { return !stack->leaks; @@ -1035,6 +1071,11 @@ void process_dump_sort_tsc(struct process *process, const char *outfile) process_dump(process, sort_tsc, skip_zero_allocations, outfile); } +void process_dump_sort_mismatched(struct process *process, const char *outfile) +{ + process_dump(process, sort_mismatched, skip_non_mismatched, outfile); +} + void process_dump_stacks(struct process *process, const char *outfile) { process_dump(process, sort_allocations, skip_none, outfile); @@ -1188,10 +1229,43 @@ void process_munmap(struct process *process, struct mt_msg *mt_msg, void *payloa } while(size); } +static int is_sane(struct rb_block *block, enum mt_operation op) +{ + switch(block->stack_node->stack->operation) { + case MT_MALLOC: + case MT_REALLOC: + case MT_REALLOC_FAILED: + case MT_MEMALIGN: + case MT_POSIX_MEMALIGN: + case MT_ALIGNED_ALLOC: + case MT_VALLOC: + case MT_PVALLOC: + if (op != MT_FREE && op != MT_REALLOC_ENTER) + return 0; + break; + case MT_NEW: + if (op != MT_DELETE) + return 0; + break; + case MT_NEW_ARRAY: + if (op != MT_DELETE_ARRAY) + return 0; + break; + case MT_MMAP: + case MT_MMAP64: + default: + return 0; + } + return 1; +} + void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) { struct rb_block *block = NULL; + uint32_t payload_len = mt_msg->payload_len; unsigned long ptr; + void *stack_data; + unsigned long stack_size; if (!process->tracing) return; @@ -1200,11 +1274,17 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) struct mt_alloc_payload_64 *mt_alloc = payload; ptr = process->get_ulong(&mt_alloc->ptr); + + stack_data = payload + sizeof(*mt_alloc); + stack_size = (payload_len - sizeof(*mt_alloc)); } else { struct mt_alloc_payload_32 *mt_alloc = payload; ptr = process->get_ulong(&mt_alloc->ptr); + + stack_data = payload + sizeof(*mt_alloc); + stack_size = (payload_len - sizeof(*mt_alloc)); } debug(DEBUG_FUNCTION, "ptr=%#lx", ptr); @@ -1219,6 +1299,14 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) abort(); } } + + if (!is_sane(block, mt_msg->operation)) { + struct rb_stack *stack = stack_add(process, process->pid, stack_data, stack_size, mt_msg->operation); + + stack->n_mismatched++; + stack->tsc = process->tsc++; + } + process_rb_delete_block(process, block); } else { @@ -1338,6 +1426,9 @@ void process_dump_sortby(struct process *process) case OPT_SORT_USAGE: _process_dump(process, sort_usage, skip_zero_allocations, options.output); break; + case OPT_SORT_MISMATCHED: + _process_dump(process, sort_mismatched, skip_non_mismatched, options.output); + break; default: _process_dump(process, sort_allocations, skip_zero_allocations, options.output); break; diff --git a/client/process.h b/client/process.h index a1ac11b..09789ff 100644 --- a/client/process.h +++ b/client/process.h @@ -109,6 +109,7 @@ void process_dump_sort_bytes_leaked(struct process *process, const char *outfile void process_dump_sort_allocations(struct process *process, const char *outfile); void process_dump_sort_total(struct process *process, const char *outfile); void process_dump_sort_tsc(struct process *process, const char *outfile); +void process_dump_sort_mismatched(struct process *process, const char *outfile); void process_dump_stacks(struct process *process, const char *outfile); void add_ignore_regex(regex_t *re); diff --git a/client/readline.c b/client/readline.c index 985b66b..60b10f5 100644 --- a/client/readline.c +++ b/client/readline.c @@ -93,6 +93,7 @@ static struct cmd_opt dump_opts[] = { { "average", 2, process_dump_sort_average, "sort by average allocation of bytes (usage / allocations)" }, { "bytes-leaked", 1, process_dump_sort_bytes_leaked, "sort by number of leaked bytes" }, { "leaks", 1, process_dump_sort_leaks, "sort by number of detected leaks" }, + { "mismatched", 1, process_dump_sort_mismatched, "sort by number of mismatched releases" }, { "stacks", 1, process_dump_stacks, "dump all stack sort by number of total allocations" }, { "total", 2, process_dump_sort_total, "sort by number of total allocations" }, { "tsc", 2, process_dump_sort_tsc, "sort by time stamp counter" }, diff --git a/memtrace.h b/memtrace.h index d78d91d..48ddbbb 100644 --- a/memtrace.h +++ b/memtrace.h @@ -65,6 +65,10 @@ enum mt_operation { MT_STOP, MT_START, MT_DETACH, + MT_NEW, + MT_NEW_ARRAY, + MT_DELETE, + MT_DELETE_ARRAY, }; struct mt_msg { diff --git a/mtrace.1 b/mtrace.1 index f1c822f..0083e53 100644 --- a/mtrace.1 +++ b/mtrace.1 @@ -42,6 +42,8 @@ mtrace \- A dynamic memory allocation tracer [\-i|\-\-interactive] [\-l|\-\-logfile \fIfilename\fR] [\-n|\-\-nocpp] +[\-N|\-\-nohwbp] +[\-S|\-\-sanity] [\-O|\-\-omit \fIfilename\fR] [\-u|\-\-user \fIusername\fR] [\-v|\-\-verbose] @@ -70,6 +72,8 @@ mtrace \- A dynamic memory allocation tracer [\-D|\-\-debug \fImask\fR] [\-l|\-\-logfile \fIfilename\fR] [\-n|\-\-nocpp] +[\-N|\-\-nohwbp] +[\-S|\-\-sanity] [\-O|\-\-omit \fIfilename\fR] [\-u|\-\-user \fIusername\fR] [\-v|\-\-verbose] @@ -266,6 +270,9 @@ since the trace will be split into to different actions. .IP "\-n, \-\-nocpp" Disable the trace of C++ allocation operators. This is safe and faster for libstdc++, since this library does call malloc() and free() inside the allocation operators. +.IP "\-N, \-\-nohwbp" +Disable the usage of hardware breakpoints. This options is intended for some +virtual machines, where hardware breakpoints not working. .IP "\-p, \-\-pid \fIpid" Attach to the process with the process ID \fIpid\fR and begin tracing. This option can be used together with passing a command to execute. @@ -301,6 +308,12 @@ Sort by number of bytes leaked (only useful with \-a option). .RE .RE .RS +\fImismatched\fR +.RS +Sort by number of mismatched releases (only useful with \-S option). +.RE +.RE +.RS \fIleaks\fR .RS Sort by number of leaked allocations (only useful with \-a option). @@ -330,6 +343,9 @@ Sort by the pseudo time stamp counter. Each stack backtrace will get an increme Sort by number of bytes in use of all open allocations. .RE .RE +.IP "\-S, \-\-sanity" +Check mismatching operations against new/new[] allocations. This options also +sets the sort-by options to mismatched. .IP "\-t, \-\-trace" Run \fBmtrace\fR in trace mode. In this mode all attached processes will run under the control of \fBmtrace\fR and all dynamic memory function calls will be traced. @@ -380,9 +396,9 @@ at any time. It accepts a maximum of three parameters: .RS Sort the output of dump by the keyword. The keyword is the same as for the \-S option (\fIallocations, \fIaverage\fR, \fIbytes-leaked\fR, \fIleaks\fR, -\fIstacks\fR, \fItotal\fR, \fItsc\fR and \fIusage\fR). See \-S option for -more details about the sortby keywords. The default sort order is -\fIallocations\fR when no sortby parameter is used. +\fImismatched\fR, \fIstacks\fR, \fItotal\fR, \fItsc\fR and \fIusage\fR). See +\-S option for more details about the sortby keywords. The default sort order +is \fIallocations\fR when no sortby parameter is used. .RE .RE .RS diff --git a/options.c b/options.c index bd6d123..3c03eef 100644 --- a/options.c +++ b/options.c @@ -48,7 +48,7 @@ #define MIN_STACK 4 #define MAX_STACK 64 -#define DEFAULT_STACK 15 +#define DEFAULT_STACK 16 #define DEFAULT_PORT 4576 static char *sockdef; @@ -89,15 +89,17 @@ static void usage(void) " -h, --help display this help and exit\n" " -i, --interactive interactive client mode\n" " -O, --omit=FILE do not place breakpoint in this file\n" - " -k, --kill abort mtrace on unexpected error conditon\n" + " -k, --kill abort mtrace due unexpected error conditon\n" " -l, --logfile use log file instead of socket connection\n" " -n, --nocpp disable trace of c++ allocation operators (faster for libstdc++)\n" + " -N, --nohwbp disable hardware breakpoint support\n" " -o, --output=FILE write the trace output to file with given name\n" " -p, --pid=PID attach to the process with the process ID pid (may be repeated)\n" " -P, --port=PORT socket port (default: " STR(DEFAULT_PORT) ")\n" " -r, --remote=addr remote use address (path, address or host)\n" " -s, --sort-by=type sort dump by type:\n" " allocations, average, bytes-leaked, leaks, stacks, total, tsc, usage\n" + " -S, --sanity check mismatching operations against new/new[] allocations\n" " -t, --trace trace mode\n" " -u, --user=USERNAME run command with the userid, groupid of username\n" " -V, --version output version information and exit\n" @@ -276,9 +278,11 @@ char **process_options(int argc, char **argv) options.opt_b = NULL; options.opt_O = NULL; options.sort_by = -1; + options.sanity = 0; options.debug = 0; options.kill = 0; options.nocpp = 0; + options.nohwbp = 0; for(;;) { int c; @@ -296,13 +300,15 @@ char **process_options(int argc, char **argv) { "interactive", 0, 0, 'i' }, { "kill", 0, 0, 'k' }, { "logfile", 1, 0, 'l' }, - { "nocpp", 1, 0, 'n' }, + { "nocpp", 0, 0, 'n' }, + { "nohwbp", 0, 0, 'N' }, { "output", 1, 0, 'o' }, { "omit", 1, 0, 'O' }, { "pid", 1, 0, 'p' }, { "port", 1, 0, 'P' }, { "remote", 1, 0, 'r' }, { "sort-by", 1, 0, 's' }, + { "sanity", 0, 0, 'S' }, { "trace", 0, 0, 't' }, { "user", 1, 0, 'u' }, { "version", 0, 0, 'V' }, @@ -312,7 +318,7 @@ char **process_options(int argc, char **argv) }; c = getopt_long(argc, argv, - "+aefhikLntVvw" + "+aefhikLnNStVvw" "b:c:d:D:F:l:o:O:p:P:r:s:u:", long_options, &option_index); @@ -414,6 +420,9 @@ char **process_options(int argc, char **argv) case 'n': options.nocpp = 1; break; + case 'N': + options.nohwbp = 1; + break; case 'p': { struct opt_p_t *tmp = malloc(sizeof(*tmp)); @@ -452,6 +461,9 @@ char **process_options(int argc, char **argv) if (!strncmp(optarg, "leaks", 1)) options.sort_by = OPT_SORT_LEAKS; else + if (!strncmp(optarg, "mismatched", 1)) + options.sort_by = OPT_SORT_MISMATCHED; + else if (!strncmp(optarg, "stacks", 1)) options.sort_by = OPT_SORT_STACKS; else @@ -468,6 +480,9 @@ char **process_options(int argc, char **argv) exit(1); } break; + case 'S': + options.sanity = 1; + break; case 't': options.trace = 1; break; @@ -545,6 +560,16 @@ char **process_options(int argc, char **argv) err_usage(); } + if (options.nohwbp) { + fprintf(stderr, "%s: client mode does not require -N\n", progname); + err_usage(); + } + + if (options.sanity) { + fprintf(stderr, "%s: client mode does not require -S\n", progname); + err_usage(); + } + if (options.user) { fprintf(stderr, "%s: user can only passed in trace mode\n", progname); err_usage(); @@ -583,6 +608,10 @@ char **process_options(int argc, char **argv) options.sort_by = -1; } } + else { + if (options.sanity) + options.sort_by = OPT_SORT_MISMATCHED; + } if (output) { if (options.interactive) { diff --git a/options.h b/options.h index b67e93b..8809904 100644 --- a/options.h +++ b/options.h @@ -37,6 +37,7 @@ #define OPT_SORT_TOTAL 5 #define OPT_SORT_TSC 6 #define OPT_SORT_USAGE 7 +#define OPT_SORT_MISMATCHED 8 struct options_t options; @@ -83,8 +84,10 @@ struct options_t { struct opt_b_t *opt_b; /* binary search path(s) */ struct opt_O_t *opt_O; /* omits path list */ int sort_by; /* sort dump in non interative and non server mode */ + int sanity; /* check mismatching operations against new/new[] allocations */ int debug; /* debug */ int nocpp; /* disable trace of c++ allocation operators */ + int nohwbp; /* disable hardware breakpoint support */ }; char **process_options(int argc, char **argv); diff --git a/report.c b/report.c index 4e207dd..a4e52d9 100644 --- a/report.c +++ b/report.c @@ -50,7 +50,8 @@ static int report_alloc64(struct task *task, enum mt_operation op, unsigned long alloc->size = (uint64_t)size; if (depth) { - alloc->data[i++] = libsym->addr; + if (libsym) + alloc->data[i++] = libsym->addr; if (backtrace_init_unwind(task) >= 0) { while(i < depth) { @@ -83,7 +84,8 @@ static int report_alloc32(struct task *task, enum mt_operation op, unsigned long alloc->size = (uint32_t)size; if (depth) { - alloc->data[i++] = libsym->addr; + if (libsym) + alloc->data[i++] = libsym->addr; if (backtrace_init_unwind(task) >= 0) { while(i < depth) { @@ -128,7 +130,7 @@ static int _null(struct task *task, struct library_symbol *libsym) return 0; } -static int _report_malloc(struct task *task, struct library_symbol *libsym) +static int _report_alloc_op(struct task *task, struct library_symbol *libsym, enum mt_operation op) { if (!server_connected()) return -1; @@ -136,17 +138,47 @@ static int _report_malloc(struct task *task, struct library_symbol *libsym) unsigned long size = fetch_param(task, 0); unsigned long ret = fetch_retval(task); - return report_alloc(task, MT_MALLOC, ret, size, options.bt_depth, libsym); + return report_alloc(task, op, ret, size, options.bt_depth, libsym); } -static int report_free(struct task *task, struct library_symbol *libsym) +static int _report_malloc(struct task *task, struct library_symbol *libsym) +{ + return _report_alloc_op(task, libsym, MT_MALLOC); +} + +static int _report_new(struct task *task, struct library_symbol *libsym) +{ + return _report_alloc_op(task, libsym, options.sanity ? MT_NEW : MT_MALLOC); +} + +static int _report_new_array(struct task *task, struct library_symbol *libsym) +{ + return _report_alloc_op(task, libsym, options.sanity ? MT_NEW_ARRAY : MT_MALLOC); +} + +static int _report_free_op(struct task *task, struct library_symbol *libsym, enum mt_operation op) { if (!server_connected()) return -1; unsigned long addr = fetch_param(task, 0); - return report_alloc(task, MT_FREE, addr, 0, 0, libsym); + return report_alloc(task, op, addr, 0, options.sanity ? options.bt_depth : 0, NULL); +} + +static int report_free(struct task *task, struct library_symbol *libsym) +{ + return _report_free_op(task, libsym, MT_FREE); +} + +static int report_delete(struct task *task, struct library_symbol *libsym) +{ + return _report_free_op(task, libsym, options.sanity ? MT_DELETE : MT_FREE); +} + +static int report_delete_array(struct task *task, struct library_symbol *libsym) +{ + return _report_free_op(task, libsym, options.sanity ? MT_DELETE_ARRAY : MT_FREE); } static int _report_realloc(struct task *task, struct library_symbol *libsym) @@ -330,35 +362,35 @@ static int _report_mremap(struct task *task, struct library_symbol *libsym) } static const struct function flist[] = { - { "malloc", "malloc", 0, NULL, _report_malloc }, - { "free", "free", 0, report_free, NULL }, - { "realloc", "realloc", 0, report_realloc, _report_realloc }, - { "calloc", "calloc", 0, NULL, _report_calloc }, - { "posix_memalign", "posix_memalign", 0, NULL, _report_posix_memalign }, - { "mmap", "mmap", 0, NULL, _report_mmap }, - { "mmap64", "mmap64", 1, NULL, _report_mmap64 }, - { "munmap", "munmap", 0, report_munmap, _null }, - { "memalign", "memalign", 0, NULL, _report_memalign }, - { "aligned_alloc", "aligned_alloc", 1, NULL, _report_aligned_alloc }, - { "valloc", "valloc", 1, NULL, _report_valloc }, - { "pvalloc", "pvalloc", 1, NULL, _report_pvalloc }, - { "mremap", "mremap", 0, report_mremap, _report_mremap }, - { "cfree", "cfree", 1, report_free, NULL }, + { "malloc", "malloc", 0, NULL, _report_malloc }, + { "free", "free", 0, report_free, NULL }, + { "realloc", "realloc", 0, report_realloc, _report_realloc }, + { "calloc", "calloc", 0, NULL, _report_calloc }, + { "posix_memalign", "posix_memalign", 0, NULL, _report_posix_memalign }, + { "mmap", "mmap", 0, NULL, _report_mmap }, + { "mmap64", "mmap64", 1, NULL, _report_mmap64 }, + { "munmap", "munmap", 0, report_munmap, _null }, + { "memalign", "memalign", 0, NULL, _report_memalign }, + { "aligned_alloc", "aligned_alloc", 1, NULL, _report_aligned_alloc }, + { "valloc", "valloc", 1, NULL, _report_valloc }, + { "pvalloc", "pvalloc", 1, NULL, _report_pvalloc }, + { "mremap", "mremap", 0, report_mremap, _report_mremap }, + { "cfree", "cfree", 1, report_free, NULL }, - { "new(unsigned int)", "_Znwj", 1, NULL, _report_malloc }, - { "new[](unsigned int)", "_Znaj", 1, NULL, _report_malloc }, - { "new(unsigned int, std::nothrow_t const&)", "_ZnwjRKSt9nothrow_t", 1, NULL, _report_malloc }, - { "new[](unsigned int, std::nothrow_t const&)", "_ZnajRKSt9nothrow_t", 1, NULL, _report_malloc }, + { "new(unsigned int)", "_Znwj", 1, NULL, _report_new }, + { "new[](unsigned int)", "_Znaj", 1, NULL, _report_new_array }, + { "new(unsigned int, std::nothrow_t const&)", "_ZnwjRKSt9nothrow_t", 1, NULL, _report_new }, + { "new[](unsigned int, std::nothrow_t const&)", "_ZnajRKSt9nothrow_t", 1, NULL, _report_new_array }, - { "new(unsigned long)", "_Znwm", 1, NULL, _report_malloc }, - { "new[](unsigned long)", "_Znam", 1, NULL, _report_malloc }, - { "new(unsigned long, std::nothrow_t const&)", "_ZnwmRKSt9nothrow_t", 1, NULL, _report_malloc }, - { "new[](unsigned long, std::nothrow_t const&)", "_ZnamRKSt9nothrow_t", 1, NULL, _report_malloc }, + { "new(unsigned long)", "_Znwm", 1, NULL, _report_new }, + { "new[](unsigned long)", "_Znam", 1, NULL, _report_new_array }, + { "new(unsigned long, std::nothrow_t const&)", "_ZnwmRKSt9nothrow_t", 1, NULL, _report_new }, + { "new[](unsigned long, std::nothrow_t const&)", "_ZnamRKSt9nothrow_t", 1, NULL, _report_new_array }, - { "delete(void*)", "_ZdlPv", 1, report_free, NULL }, - { "delete[](void*)", "_ZdaPv", 1, report_free, NULL }, - { "delete(void*, std::nothrow_t const&)", "_ZdlPvRKSt9nothrow_t", 1, report_free, NULL }, - { "delete[](void*, std::nothrow_t const&)", "_ZdaPvRKSt9nothrow_t", 1, report_free, NULL }, + { "delete(void*)", "_ZdlPv", 1, report_delete, NULL }, + { "delete[](void*)", "_ZdaPv", 1, report_delete_array, NULL }, + { "delete(void*, std::nothrow_t const&)", "_ZdlPvRKSt9nothrow_t", 1, report_delete, NULL }, + { "delete[](void*, std::nothrow_t const&)", "_ZdaPvRKSt9nothrow_t", 1, report_delete_array, NULL }, }; const struct function *flist_matches_symbol(const char *sym_name)