mirror of
https://github.com/sstefani/mtrace.git
synced 2025-12-06 16:56:41 +08:00
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:
parent
96cd4a6ce8
commit
802a1a3b22
@ -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;
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
117
client/process.c
117
client/process.c
@ -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,20 +959,31 @@ 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 (dump_printf(
|
if (!stack->n_mismatched) {
|
||||||
"Stack (%s):\n"
|
if (dump_printf(
|
||||||
" bytes used: %llu\n"
|
"Stack (%s):\n"
|
||||||
" number of open allocations: %llu\n"
|
" bytes used: %llu\n"
|
||||||
" total number of allocations: %llu\n",
|
" number of open allocations: %llu\n"
|
||||||
str_operation(stack->stack->operation),
|
" total number of allocations: %llu\n",
|
||||||
stack->bytes_used,
|
str_operation(stack->stack->operation),
|
||||||
stack->n_allocations,
|
stack->bytes_used,
|
||||||
stack->total_allocations
|
stack->n_allocations,
|
||||||
) == -1)
|
stack->total_allocations
|
||||||
break;
|
) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
if (stack->leaks) {
|
if (stack->leaks) {
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (dump_printf(
|
||||||
|
"Stack (%s):\n"
|
||||||
|
" total number of mismatches: %llu\n",
|
||||||
|
str_operation(stack->stack->operation),
|
||||||
|
stack->n_mismatched
|
||||||
|
) == -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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" },
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
22
mtrace.1
22
mtrace.1
@ -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
|
||||||
|
|||||||
37
options.c
37
options.c
@ -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) {
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
96
report.c
96
report.c
@ -50,7 +50,8 @@ 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) {
|
||||||
alloc->data[i++] = libsym->addr;
|
if (libsym)
|
||||||
|
alloc->data[i++] = libsym->addr;
|
||||||
|
|
||||||
if (backtrace_init_unwind(task) >= 0) {
|
if (backtrace_init_unwind(task) >= 0) {
|
||||||
while(i < depth) {
|
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;
|
alloc->size = (uint32_t)size;
|
||||||
|
|
||||||
if (depth) {
|
if (depth) {
|
||||||
alloc->data[i++] = libsym->addr;
|
if (libsym)
|
||||||
|
alloc->data[i++] = libsym->addr;
|
||||||
|
|
||||||
if (backtrace_init_unwind(task) >= 0) {
|
if (backtrace_init_unwind(task) >= 0) {
|
||||||
while(i < depth) {
|
while(i < depth) {
|
||||||
@ -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)
|
||||||
@ -330,35 +362,35 @@ static int _report_mremap(struct task *task, struct library_symbol *libsym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct function flist[] = {
|
static const struct function flist[] = {
|
||||||
{ "malloc", "malloc", 0, NULL, _report_malloc },
|
{ "malloc", "malloc", 0, NULL, _report_malloc },
|
||||||
{ "free", "free", 0, report_free, NULL },
|
{ "free", "free", 0, report_free, NULL },
|
||||||
{ "realloc", "realloc", 0, report_realloc, _report_realloc },
|
{ "realloc", "realloc", 0, report_realloc, _report_realloc },
|
||||||
{ "calloc", "calloc", 0, NULL, _report_calloc },
|
{ "calloc", "calloc", 0, NULL, _report_calloc },
|
||||||
{ "posix_memalign", "posix_memalign", 0, NULL, _report_posix_memalign },
|
{ "posix_memalign", "posix_memalign", 0, NULL, _report_posix_memalign },
|
||||||
{ "mmap", "mmap", 0, NULL, _report_mmap },
|
{ "mmap", "mmap", 0, NULL, _report_mmap },
|
||||||
{ "mmap64", "mmap64", 1, NULL, _report_mmap64 },
|
{ "mmap64", "mmap64", 1, NULL, _report_mmap64 },
|
||||||
{ "munmap", "munmap", 0, report_munmap, _null },
|
{ "munmap", "munmap", 0, report_munmap, _null },
|
||||||
{ "memalign", "memalign", 0, NULL, _report_memalign },
|
{ "memalign", "memalign", 0, NULL, _report_memalign },
|
||||||
{ "aligned_alloc", "aligned_alloc", 1, NULL, _report_aligned_alloc },
|
{ "aligned_alloc", "aligned_alloc", 1, NULL, _report_aligned_alloc },
|
||||||
{ "valloc", "valloc", 1, NULL, _report_valloc },
|
{ "valloc", "valloc", 1, NULL, _report_valloc },
|
||||||
{ "pvalloc", "pvalloc", 1, NULL, _report_pvalloc },
|
{ "pvalloc", "pvalloc", 1, NULL, _report_pvalloc },
|
||||||
{ "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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user