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.
This commit is contained in:
Stefani Seibold 2015-10-28 07:57:53 +01:00
parent 96cd4a6ce8
commit 802a1a3b22
10 changed files with 236 additions and 52 deletions

View File

@ -329,6 +329,9 @@ struct breakpoint *breakpoint_new_ext(struct task *task, arch_addr_t addr, struc
if (bp == NULL) if (bp == NULL)
goto fail1; goto fail1;
if (options.nohwbp)
bp_type = BP_SW;
bp->on_hit = NULL; bp->on_hit = NULL;
bp->libsym = libsym; bp->libsym = libsym;
bp->addr = addr; bp->addr = addr;

View File

@ -427,10 +427,14 @@ static int client_func(void)
case MT_PVALLOC: case MT_PVALLOC:
case MT_MMAP: case MT_MMAP:
case MT_MMAP64: case MT_MMAP64:
case MT_NEW:
case MT_NEW_ARRAY:
process_alloc(process, &mt_msg, payload); process_alloc(process, &mt_msg, payload);
break; break;
case MT_REALLOC_ENTER: case MT_REALLOC_ENTER:
case MT_FREE: case MT_FREE:
case MT_DELETE:
case MT_DELETE_ARRAY:
process_free(process, &mt_msg, payload); process_free(process, &mt_msg, payload);
break; break;
case MT_MUNMAP: case MT_MUNMAP:

View File

@ -70,6 +70,7 @@ struct rb_stack {
unsigned long long bytes_used; unsigned long long bytes_used;
unsigned long long bytes_leaked; unsigned long long bytes_leaked;
unsigned long long tsc; unsigned long long tsc;
unsigned long long n_mismatched;
}; };
struct map { struct map {
@ -139,6 +140,14 @@ static const char *str_operation(enum mt_operation operation)
return "free"; return "free";
case MT_MUNMAP: case MT_MUNMAP:
return "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: default:
break; break;
} }
@ -422,6 +431,7 @@ static struct rb_stack *stack_clone(struct process *process, struct rb_stack *st
this->leaks = stack_node->leaks; this->leaks = stack_node->leaks;
this->n_allocations = stack_node->n_allocations; this->n_allocations = stack_node->n_allocations;
this->n_mismatched = stack_node->n_mismatched;
this->total_allocations = stack_node->total_allocations; this->total_allocations = stack_node->total_allocations;
this->bytes_used = stack_node->bytes_used; this->bytes_used = stack_node->bytes_used;
this->bytes_leaked = stack_node->bytes_leaked; 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); memcpy(stack->addrs, addrs, stack_size);
this->n_allocations = 0; this->n_allocations = 0;
this->n_mismatched = 0;
this->total_allocations = 0; this->total_allocations = 0;
this->bytes_used = 0; this->bytes_used = 0;
this->leaks = 0; this->leaks = 0;
@ -832,6 +843,15 @@ static int sort_tsc(const struct rb_stack **p, const struct rb_stack **q)
return 0; 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) static int sort_usage(const struct rb_stack **p, const struct rb_stack **q)
{ {
if ((*p)->bytes_used > (*q)->bytes_used) if ((*p)->bytes_used > (*q)->bytes_used)
@ -939,6 +959,7 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb
struct rb_stack *stack = arr[i]; struct rb_stack *stack = arr[i];
if (!skipfunc(stack)) { if (!skipfunc(stack)) {
if (!stack->n_mismatched) {
if (dump_printf( if (dump_printf(
"Stack (%s):\n" "Stack (%s):\n"
" bytes used: %llu\n" " bytes used: %llu\n"
@ -955,6 +976,16 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb
if (dump_printf( " leaked allocations: %lu (%llu bytes)\n", stack->leaks, stack->bytes_leaked) == -1) if (dump_printf( " leaked allocations: %lu (%llu bytes)\n", stack->leaks, stack->bytes_leaked) == -1)
break; break;
} }
}
else {
if (dump_printf(
"Stack (%s):\n"
" total number of mismatches: %llu\n",
str_operation(stack->stack->operation),
stack->n_mismatched
) == -1)
break;
}
if (dump_printf(" tsc: %llu\n", stack->tsc) == -1) if (dump_printf(" tsc: %llu\n", stack->tsc) == -1)
break; break;
@ -995,6 +1026,11 @@ static int skip_zero_allocations(struct rb_stack *stack)
return !stack->n_allocations; 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) static int skip_zero_leaks(struct rb_stack *stack)
{ {
return !stack->leaks; 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); 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) void process_dump_stacks(struct process *process, const char *outfile)
{ {
process_dump(process, sort_allocations, skip_none, 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); } 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) void process_free(struct process *process, struct mt_msg *mt_msg, void *payload)
{ {
struct rb_block *block = NULL; struct rb_block *block = NULL;
uint32_t payload_len = mt_msg->payload_len;
unsigned long ptr; unsigned long ptr;
void *stack_data;
unsigned long stack_size;
if (!process->tracing) if (!process->tracing)
return; 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; struct mt_alloc_payload_64 *mt_alloc = payload;
ptr = process->get_ulong(&mt_alloc->ptr); ptr = process->get_ulong(&mt_alloc->ptr);
stack_data = payload + sizeof(*mt_alloc);
stack_size = (payload_len - sizeof(*mt_alloc));
} }
else { else {
struct mt_alloc_payload_32 *mt_alloc = payload; struct mt_alloc_payload_32 *mt_alloc = payload;
ptr = process->get_ulong(&mt_alloc->ptr); 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); debug(DEBUG_FUNCTION, "ptr=%#lx", ptr);
@ -1219,6 +1299,14 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload)
abort(); 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); process_rb_delete_block(process, block);
} }
else { else {
@ -1338,6 +1426,9 @@ void process_dump_sortby(struct process *process)
case OPT_SORT_USAGE: case OPT_SORT_USAGE:
_process_dump(process, sort_usage, skip_zero_allocations, options.output); _process_dump(process, sort_usage, skip_zero_allocations, options.output);
break; break;
case OPT_SORT_MISMATCHED:
_process_dump(process, sort_mismatched, skip_non_mismatched, options.output);
break;
default: default:
_process_dump(process, sort_allocations, skip_zero_allocations, options.output); _process_dump(process, sort_allocations, skip_zero_allocations, options.output);
break; break;

View File

@ -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_allocations(struct process *process, const char *outfile);
void process_dump_sort_total(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_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 process_dump_stacks(struct process *process, const char *outfile);
void add_ignore_regex(regex_t *re); void add_ignore_regex(regex_t *re);

View File

@ -93,6 +93,7 @@ static struct cmd_opt dump_opts[] = {
{ "average", 2, process_dump_sort_average, "sort by average allocation of bytes (usage / allocations)" }, { "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" }, { "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" }, { "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" }, { "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" }, { "total", 2, process_dump_sort_total, "sort by number of total allocations" },
{ "tsc", 2, process_dump_sort_tsc, "sort by time stamp counter" }, { "tsc", 2, process_dump_sort_tsc, "sort by time stamp counter" },

View File

@ -65,6 +65,10 @@ enum mt_operation {
MT_STOP, MT_STOP,
MT_START, MT_START,
MT_DETACH, MT_DETACH,
MT_NEW,
MT_NEW_ARRAY,
MT_DELETE,
MT_DELETE_ARRAY,
}; };
struct mt_msg { struct mt_msg {

View File

@ -42,6 +42,8 @@ mtrace \- A dynamic memory allocation tracer
[\-i|\-\-interactive] [\-i|\-\-interactive]
[\-l|\-\-logfile \fIfilename\fR] [\-l|\-\-logfile \fIfilename\fR]
[\-n|\-\-nocpp] [\-n|\-\-nocpp]
[\-N|\-\-nohwbp]
[\-S|\-\-sanity]
[\-O|\-\-omit \fIfilename\fR] [\-O|\-\-omit \fIfilename\fR]
[\-u|\-\-user \fIusername\fR] [\-u|\-\-user \fIusername\fR]
[\-v|\-\-verbose] [\-v|\-\-verbose]
@ -70,6 +72,8 @@ mtrace \- A dynamic memory allocation tracer
[\-D|\-\-debug \fImask\fR] [\-D|\-\-debug \fImask\fR]
[\-l|\-\-logfile \fIfilename\fR] [\-l|\-\-logfile \fIfilename\fR]
[\-n|\-\-nocpp] [\-n|\-\-nocpp]
[\-N|\-\-nohwbp]
[\-S|\-\-sanity]
[\-O|\-\-omit \fIfilename\fR] [\-O|\-\-omit \fIfilename\fR]
[\-u|\-\-user \fIusername\fR] [\-u|\-\-user \fIusername\fR]
[\-v|\-\-verbose] [\-v|\-\-verbose]
@ -266,6 +270,9 @@ since the trace will be split into to different actions.
.IP "\-n, \-\-nocpp" .IP "\-n, \-\-nocpp"
Disable the trace of C++ allocation operators. This is safe and faster for libstdc++, 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. 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" .IP "\-p, \-\-pid \fIpid"
Attach to the process with the process ID \fIpid\fR and begin tracing. 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. 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
.RE .RE
.RS .RS
\fImismatched\fR
.RS
Sort by number of mismatched releases (only useful with \-S option).
.RE
.RE
.RS
\fIleaks\fR \fIleaks\fR
.RS .RS
Sort by number of leaked allocations (only useful with \-a option). 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. Sort by number of bytes in use of all open allocations.
.RE .RE
.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" .IP "\-t, \-\-trace"
Run \fBmtrace\fR in trace mode. In this mode all attached processes will run under 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. 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 .RS
Sort the output of dump by the keyword. The keyword is the same as for the 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, \-S option (\fIallocations, \fIaverage\fR, \fIbytes-leaked\fR, \fIleaks\fR,
\fIstacks\fR, \fItotal\fR, \fItsc\fR and \fIusage\fR). See \-S option for \fImismatched\fR, \fIstacks\fR, \fItotal\fR, \fItsc\fR and \fIusage\fR). See
more details about the sortby keywords. The default sort order is \-S option for more details about the sortby keywords. The default sort order
\fIallocations\fR when no sortby parameter is used. is \fIallocations\fR when no sortby parameter is used.
.RE .RE
.RE .RE
.RS .RS

View File

@ -48,7 +48,7 @@
#define MIN_STACK 4 #define MIN_STACK 4
#define MAX_STACK 64 #define MAX_STACK 64
#define DEFAULT_STACK 15 #define DEFAULT_STACK 16
#define DEFAULT_PORT 4576 #define DEFAULT_PORT 4576
static char *sockdef; static char *sockdef;
@ -89,15 +89,17 @@ static void usage(void)
" -h, --help display this help and exit\n" " -h, --help display this help and exit\n"
" -i, --interactive interactive client mode\n" " -i, --interactive interactive client mode\n"
" -O, --omit=FILE do not place breakpoint in this file\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" " -l, --logfile use log file instead of socket connection\n"
" -n, --nocpp disable trace of c++ allocation operators (faster for libstdc++)\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" " -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, --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" " -P, --port=PORT socket port (default: " STR(DEFAULT_PORT) ")\n"
" -r, --remote=addr remote use address (path, address or host)\n" " -r, --remote=addr remote use address (path, address or host)\n"
" -s, --sort-by=type sort dump by type:\n" " -s, --sort-by=type sort dump by type:\n"
" allocations, average, bytes-leaked, leaks, stacks, total, tsc, usage\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" " -t, --trace trace mode\n"
" -u, --user=USERNAME run command with the userid, groupid of username\n" " -u, --user=USERNAME run command with the userid, groupid of username\n"
" -V, --version output version information and exit\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_b = NULL;
options.opt_O = NULL; options.opt_O = NULL;
options.sort_by = -1; options.sort_by = -1;
options.sanity = 0;
options.debug = 0; options.debug = 0;
options.kill = 0; options.kill = 0;
options.nocpp = 0; options.nocpp = 0;
options.nohwbp = 0;
for(;;) { for(;;) {
int c; int c;
@ -296,13 +300,15 @@ char **process_options(int argc, char **argv)
{ "interactive", 0, 0, 'i' }, { "interactive", 0, 0, 'i' },
{ "kill", 0, 0, 'k' }, { "kill", 0, 0, 'k' },
{ "logfile", 1, 0, 'l' }, { "logfile", 1, 0, 'l' },
{ "nocpp", 1, 0, 'n' }, { "nocpp", 0, 0, 'n' },
{ "nohwbp", 0, 0, 'N' },
{ "output", 1, 0, 'o' }, { "output", 1, 0, 'o' },
{ "omit", 1, 0, 'O' }, { "omit", 1, 0, 'O' },
{ "pid", 1, 0, 'p' }, { "pid", 1, 0, 'p' },
{ "port", 1, 0, 'P' }, { "port", 1, 0, 'P' },
{ "remote", 1, 0, 'r' }, { "remote", 1, 0, 'r' },
{ "sort-by", 1, 0, 's' }, { "sort-by", 1, 0, 's' },
{ "sanity", 0, 0, 'S' },
{ "trace", 0, 0, 't' }, { "trace", 0, 0, 't' },
{ "user", 1, 0, 'u' }, { "user", 1, 0, 'u' },
{ "version", 0, 0, 'V' }, { "version", 0, 0, 'V' },
@ -312,7 +318,7 @@ char **process_options(int argc, char **argv)
}; };
c = getopt_long(argc, argv, c = getopt_long(argc, argv,
"+aefhikLntVvw" "+aefhikLnNStVvw"
"b:c:d:D:F:l:o:O:p:P:r:s:u:", "b:c:d:D:F:l:o:O:p:P:r:s:u:",
long_options, long_options,
&option_index); &option_index);
@ -414,6 +420,9 @@ char **process_options(int argc, char **argv)
case 'n': case 'n':
options.nocpp = 1; options.nocpp = 1;
break; break;
case 'N':
options.nohwbp = 1;
break;
case 'p': case 'p':
{ {
struct opt_p_t *tmp = malloc(sizeof(*tmp)); struct opt_p_t *tmp = malloc(sizeof(*tmp));
@ -452,6 +461,9 @@ char **process_options(int argc, char **argv)
if (!strncmp(optarg, "leaks", 1)) if (!strncmp(optarg, "leaks", 1))
options.sort_by = OPT_SORT_LEAKS; options.sort_by = OPT_SORT_LEAKS;
else else
if (!strncmp(optarg, "mismatched", 1))
options.sort_by = OPT_SORT_MISMATCHED;
else
if (!strncmp(optarg, "stacks", 1)) if (!strncmp(optarg, "stacks", 1))
options.sort_by = OPT_SORT_STACKS; options.sort_by = OPT_SORT_STACKS;
else else
@ -468,6 +480,9 @@ char **process_options(int argc, char **argv)
exit(1); exit(1);
} }
break; break;
case 'S':
options.sanity = 1;
break;
case 't': case 't':
options.trace = 1; options.trace = 1;
break; break;
@ -545,6 +560,16 @@ char **process_options(int argc, char **argv)
err_usage(); 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) { if (options.user) {
fprintf(stderr, "%s: user can only passed in trace mode\n", progname); fprintf(stderr, "%s: user can only passed in trace mode\n", progname);
err_usage(); err_usage();
@ -583,6 +608,10 @@ char **process_options(int argc, char **argv)
options.sort_by = -1; options.sort_by = -1;
} }
} }
else {
if (options.sanity)
options.sort_by = OPT_SORT_MISMATCHED;
}
if (output) { if (output) {
if (options.interactive) { if (options.interactive) {

View File

@ -37,6 +37,7 @@
#define OPT_SORT_TOTAL 5 #define OPT_SORT_TOTAL 5
#define OPT_SORT_TSC 6 #define OPT_SORT_TSC 6
#define OPT_SORT_USAGE 7 #define OPT_SORT_USAGE 7
#define OPT_SORT_MISMATCHED 8
struct options_t options; struct options_t options;
@ -83,8 +84,10 @@ struct options_t {
struct opt_b_t *opt_b; /* binary search path(s) */ struct opt_b_t *opt_b; /* binary search path(s) */
struct opt_O_t *opt_O; /* omits path list */ struct opt_O_t *opt_O; /* omits path list */
int sort_by; /* sort dump in non interative and non server mode */ 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 debug; /* debug */
int nocpp; /* disable trace of c++ allocation operators */ int nocpp; /* disable trace of c++ allocation operators */
int nohwbp; /* disable hardware breakpoint support */
}; };
char **process_options(int argc, char **argv); char **process_options(int argc, char **argv);

View File

@ -50,6 +50,7 @@ static int report_alloc64(struct task *task, enum mt_operation op, unsigned long
alloc->size = (uint64_t)size; alloc->size = (uint64_t)size;
if (depth) { if (depth) {
if (libsym)
alloc->data[i++] = libsym->addr; alloc->data[i++] = libsym->addr;
if (backtrace_init_unwind(task) >= 0) { if (backtrace_init_unwind(task) >= 0) {
@ -83,6 +84,7 @@ static int report_alloc32(struct task *task, enum mt_operation op, unsigned long
alloc->size = (uint32_t)size; alloc->size = (uint32_t)size;
if (depth) { if (depth) {
if (libsym)
alloc->data[i++] = libsym->addr; alloc->data[i++] = libsym->addr;
if (backtrace_init_unwind(task) >= 0) { if (backtrace_init_unwind(task) >= 0) {
@ -128,7 +130,7 @@ static int _null(struct task *task, struct library_symbol *libsym)
return 0; 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()) if (!server_connected())
return -1; 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 size = fetch_param(task, 0);
unsigned long ret = fetch_retval(task); 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()) if (!server_connected())
return -1; return -1;
unsigned long addr = fetch_param(task, 0); 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) static int _report_realloc(struct task *task, struct library_symbol *libsym)
@ -345,20 +377,20 @@ static const struct function flist[] = {
{ "mremap", "mremap", 0, report_mremap, _report_mremap }, { "mremap", "mremap", 0, report_mremap, _report_mremap },
{ "cfree", "cfree", 1, report_free, NULL }, { "cfree", "cfree", 1, report_free, NULL },
{ "new(unsigned int)", "_Znwj", 1, NULL, _report_malloc }, { "new(unsigned int)", "_Znwj", 1, NULL, _report_new },
{ "new[](unsigned int)", "_Znaj", 1, NULL, _report_malloc }, { "new[](unsigned int)", "_Znaj", 1, NULL, _report_new_array },
{ "new(unsigned int, std::nothrow_t const&)", "_ZnwjRKSt9nothrow_t", 1, NULL, _report_malloc }, { "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_malloc }, { "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)", "_Znwm", 1, NULL, _report_new },
{ "new[](unsigned long)", "_Znam", 1, NULL, _report_malloc }, { "new[](unsigned long)", "_Znam", 1, NULL, _report_new_array },
{ "new(unsigned long, std::nothrow_t const&)", "_ZnwmRKSt9nothrow_t", 1, NULL, _report_malloc }, { "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_malloc }, { "new[](unsigned long, std::nothrow_t const&)", "_ZnamRKSt9nothrow_t", 1, NULL, _report_new_array },
{ "delete(void*)", "_ZdlPv", 1, report_free, NULL }, { "delete(void*)", "_ZdlPv", 1, report_delete, NULL },
{ "delete[](void*)", "_ZdaPv", 1, report_free, NULL }, { "delete[](void*)", "_ZdaPv", 1, report_delete_array, NULL },
{ "delete(void*, std::nothrow_t const&)", "_ZdlPvRKSt9nothrow_t", 1, report_free, NULL }, { "delete(void*, std::nothrow_t const&)", "_ZdlPvRKSt9nothrow_t", 1, report_delete, NULL },
{ "delete[](void*, std::nothrow_t const&)", "_ZdaPvRKSt9nothrow_t", 1, report_free, NULL }, { "delete[](void*, std::nothrow_t const&)", "_ZdaPvRKSt9nothrow_t", 1, report_delete_array, NULL },
}; };
const struct function *flist_matches_symbol(const char *sym_name) const struct function *flist_matches_symbol(const char *sym_name)