mirror of
https://github.com/sstefani/mtrace.git
synced 2025-12-06 08:46:41 +08:00
features and fixes
add ignore regex list fix minor bugs
This commit is contained in:
parent
92e40e9ec5
commit
31e81d4f25
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,4 +29,5 @@ stamp-h1
|
||||
libtool
|
||||
docross
|
||||
doremote
|
||||
dokvm
|
||||
mtrace
|
||||
|
||||
1
TODO
1
TODO
@ -3,7 +3,6 @@ arm thumb support
|
||||
dwarf debug support
|
||||
arm & ppc hw bp support
|
||||
dwarf caching
|
||||
regex
|
||||
invalid stack trace cache for unmapped libraries
|
||||
restore REALLOC_TRY when realloc fails
|
||||
manual page
|
||||
|
||||
184
client/client.c
184
client/client.c
@ -32,6 +32,7 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "binfile.h"
|
||||
#include "common.h"
|
||||
@ -57,6 +58,167 @@ static int first_pid;
|
||||
static struct memtrace_info mt_info;
|
||||
static struct thread *thread;
|
||||
|
||||
static unsigned long skip_nl(const char *p, unsigned long n)
|
||||
{
|
||||
unsigned long c = 0;
|
||||
|
||||
while(n > c) {
|
||||
if (p[c] != '\n' && p[c] != '\r')
|
||||
break;
|
||||
++c;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long get_line(const char *p, unsigned long n)
|
||||
{
|
||||
unsigned long c = 0;
|
||||
|
||||
while(n > c) {
|
||||
if (p[c] == '\n' || p[c] == '\r')
|
||||
break;
|
||||
++c;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long skip_space(const char *p, unsigned long n)
|
||||
{
|
||||
unsigned long c = 0;
|
||||
|
||||
while(n > c) {
|
||||
if (p[c] != ' ' && p[c] != '\t')
|
||||
break;
|
||||
++c;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long trim_space(const char *p, unsigned long n)
|
||||
{
|
||||
unsigned long c = n;
|
||||
|
||||
while(c) {
|
||||
if (p[c - 1] != ' ' && p[c - 1] != '\t')
|
||||
break;
|
||||
--c;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static unsigned long cmp_n(const char *p, const char *str, unsigned long n)
|
||||
{
|
||||
unsigned long c = 0;
|
||||
|
||||
while(n > c) {
|
||||
if (!str[c]) {
|
||||
if (p[c] == ' ' || p[c] == '\t')
|
||||
return c;
|
||||
break;
|
||||
}
|
||||
|
||||
if (p[c] != str[c])
|
||||
break;
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _parse_config(const char *filename, const char *p, unsigned long n)
|
||||
{
|
||||
unsigned long c, l;
|
||||
|
||||
for(;;) {
|
||||
c = skip_nl(p, n);
|
||||
|
||||
p += c;
|
||||
n -= c;
|
||||
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
l = get_line(p, n);
|
||||
|
||||
c = skip_space(p, l);
|
||||
|
||||
p += c;
|
||||
n -= c;
|
||||
l -= c;
|
||||
|
||||
if (!n)
|
||||
break;
|
||||
|
||||
if (*p != '#') {
|
||||
c = cmp_n(p, "ignore", l);
|
||||
if (c) {
|
||||
c += skip_space(p + c, l - c);
|
||||
|
||||
if (c != l) {
|
||||
int ret;
|
||||
regex_t re;
|
||||
char *regex;
|
||||
|
||||
regex = strndup(p + c, trim_space(p + c, l - c));
|
||||
|
||||
ret = regcomp(&re, regex, REG_NOSUB);
|
||||
if (ret) {
|
||||
char errbuf[128];
|
||||
|
||||
regerror(ret, &re, errbuf, sizeof(errbuf));
|
||||
|
||||
fprintf(stderr, "%s: invalid regular expression: `%s' (%s)\n", filename, regex, errbuf);
|
||||
}
|
||||
else
|
||||
add_ignore_regex(&re);
|
||||
|
||||
free(regex);
|
||||
}
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s: invalid line `%.*s'\n", filename, (int)l, p);
|
||||
}
|
||||
|
||||
p += l;
|
||||
n -= l;
|
||||
|
||||
if (!n)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_config(const char *filename)
|
||||
{
|
||||
char *p;
|
||||
struct stat statbuf;
|
||||
int fd;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd == -1)
|
||||
fatal("could not open config file: `%s' (%s)", filename, strerror(errno));
|
||||
|
||||
if (fstat(fd, &statbuf) == -1)
|
||||
fatal("could not read config file: `%s' (%s)", filename, strerror(errno));
|
||||
|
||||
if (statbuf.st_size) {
|
||||
p = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (p == MAP_FAILED)
|
||||
fatal("could map config file: `%s' (%s)", filename, strerror(errno));
|
||||
|
||||
_parse_config(filename, p, statbuf.st_size);
|
||||
|
||||
munmap(p, statbuf.st_size);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rb_process *pid_rb_search(struct rb_root *root, pid_t pid)
|
||||
{
|
||||
struct rb_node *node = root->rb_node;
|
||||
@ -379,24 +541,35 @@ void client_remove_process(struct process *process)
|
||||
}
|
||||
|
||||
|
||||
void _client_init(int do_trace)
|
||||
static int client_init(int do_trace)
|
||||
{
|
||||
struct opt_F_t *p;
|
||||
|
||||
pid_table = RB_ROOT;
|
||||
first_pid = 0;
|
||||
|
||||
mt_info.version = MEMTRACE_SI_VERSION;
|
||||
mt_info.mode = 0;
|
||||
mt_info.do_trace = do_trace;
|
||||
mt_info.stack_depth = 0;
|
||||
|
||||
for(p = options.opt_F; p; p = p->next) {
|
||||
if (parse_config(p->filename) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int client_start(void)
|
||||
{
|
||||
_client_init(0);
|
||||
if (client_init(0) < 0)
|
||||
return -1;
|
||||
|
||||
client_fd = connect_to(options.client, options.port);
|
||||
|
||||
if (client_fd == -1) {
|
||||
fprintf(stderr, "could not connect: %s:%s", options.client, options.port);
|
||||
fprintf(stderr, "could not connect: %s:%s\n", options.client, options.port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -434,14 +607,15 @@ void *client_thread(void *unused)
|
||||
|
||||
int client_start_pair(int handle)
|
||||
{
|
||||
if (client_init(1) < 0)
|
||||
return -1;
|
||||
|
||||
thread = thread_new();
|
||||
if (!thread)
|
||||
return -1;
|
||||
|
||||
client_fd = handle;
|
||||
|
||||
_client_init(1);
|
||||
|
||||
if (thread_start(thread, client_thread, NULL))
|
||||
fatal("could not start thread (%s)", strerror(errno));
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ struct stack {
|
||||
uint32_t entries;
|
||||
char **syms;
|
||||
enum mt_operation operation;
|
||||
unsigned int ignore:1;
|
||||
};
|
||||
|
||||
struct rb_stack {
|
||||
@ -77,9 +78,37 @@ struct map {
|
||||
char *filename;
|
||||
char *realpath;
|
||||
struct bin_file *binfile;
|
||||
int ignore;
|
||||
unsigned int ignore:1;
|
||||
};
|
||||
|
||||
struct regex_list {
|
||||
regex_t re;
|
||||
struct regex_list *next;
|
||||
};
|
||||
|
||||
static struct regex_list *regex_ignore_list;
|
||||
static struct regex_list *regex_ignore_last;
|
||||
|
||||
void add_ignore_regex(regex_t *re)
|
||||
{
|
||||
struct regex_list *tmp = malloc(sizeof(*tmp));
|
||||
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tmp->re = *re;
|
||||
tmp->next = NULL;
|
||||
|
||||
if (regex_ignore_last)
|
||||
regex_ignore_last->next = tmp;
|
||||
regex_ignore_last = tmp;
|
||||
|
||||
if (!regex_ignore_list)
|
||||
regex_ignore_list = tmp;
|
||||
}
|
||||
|
||||
static const char *str_operation(enum mt_operation operation)
|
||||
{
|
||||
switch(operation) {
|
||||
@ -337,6 +366,21 @@ static void stack_resolv(struct process *process, struct stack *stack)
|
||||
|
||||
addrs += process->ptr_size;
|
||||
}
|
||||
|
||||
if (regex_ignore_list) {
|
||||
for(i = 0; i < stack->entries; ++i) {
|
||||
struct regex_list *p;
|
||||
|
||||
for(p = regex_ignore_list; p; p = p->next)
|
||||
if (stack->syms[i] && !regexec(&p->re, stack->syms[i], 0, NULL, 0)) {
|
||||
stack->ignore = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stack->ignore)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void stack_unref(struct stack *stack)
|
||||
@ -439,6 +483,7 @@ static struct rb_stack *stack_add(struct process *process, pid_t pid, void *addr
|
||||
stack->entries = stack_size / process->ptr_size;
|
||||
stack->syms = NULL;
|
||||
stack->operation = operation;
|
||||
stack->ignore = 0;
|
||||
|
||||
memcpy(stack->addrs, addrs, stack_size);
|
||||
|
||||
@ -859,7 +904,7 @@ static void _process_dump(struct process *process, int (*sortby)(const struct rb
|
||||
for(i = 0; i < process->stack_trees; ++i) {
|
||||
struct rb_stack *stack = arr[i];
|
||||
|
||||
if (!skipfunc(stack)) {
|
||||
if (!skipfunc(stack) && !stack->stack->ignore) {
|
||||
if (dump_printf(
|
||||
"Stack (%s):\n"
|
||||
" bytes used: %llu\n"
|
||||
@ -1056,11 +1101,8 @@ void process_munmap(struct process *process, struct mt_msg *mt_msg, void *payloa
|
||||
break;
|
||||
|
||||
if (!is_mmap(block->stack_node->stack->operation)) {
|
||||
if (options.verbose > 1)
|
||||
fprintf(stderr, ">>> block missmatch MAP<>MALLOC %#lx found\n", ptr);
|
||||
if (options.wait)
|
||||
abort();
|
||||
break;
|
||||
fprintf(stderr, ">>> block missmatch MAP<>MALLOC %#lx found\n", ptr);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (block->addr >= ptr) {
|
||||
@ -1139,18 +1181,14 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload)
|
||||
block = process_rb_search(&process->block_table, ptr);
|
||||
if (block) {
|
||||
if (is_mmap(block->stack_node->stack->operation)) {
|
||||
if (options.verbose > 1)
|
||||
fprintf(stderr, ">>> block missmatch MAP<>MALLOC %#lx found\n", ptr);
|
||||
if (options.wait)
|
||||
abort();
|
||||
fprintf(stderr, ">>> block missmatch MAP<>MALLOC %#lx found\n", ptr);
|
||||
abort();
|
||||
}
|
||||
process_rb_delete_block(process, block);
|
||||
}
|
||||
else {
|
||||
if (!process->attached) {
|
||||
if (!process->attached)
|
||||
fprintf(stderr, ">>> block %#lx not found (pid=%d, tid=%d)\n", ptr, process->pid, mt_msg->tid);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1191,12 +1229,8 @@ void process_alloc(struct process *process, struct mt_msg *mt_msg, void *payload
|
||||
|
||||
block = process_rb_search(&process->block_table, ptr);
|
||||
if (block) {
|
||||
if (options.verbose > 1) {
|
||||
fprintf(stderr, ">>> block collison %s ptr %#lx size %lu pid %d tid %d\n", str_operation(mt_msg->operation), ptr, size, process->pid, mt_msg->tid);
|
||||
abort();
|
||||
}
|
||||
|
||||
process_rb_delete_block(process, block);
|
||||
fprintf(stderr, ">>> block collison %s ptr %#lx size %lu pid %d tid %d\n", str_operation(mt_msg->operation), ptr, size, process->pid, mt_msg->tid);
|
||||
abort();
|
||||
}
|
||||
|
||||
struct rb_stack *stack = stack_add(process, process->pid, stack_data, stack_size, mt_msg->operation);
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
#ifndef _INC_CLIENT_PROCESS_H
|
||||
#define _INC_CLIENT_PROCESS_H
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
#include "list.h"
|
||||
#include "memtrace.h"
|
||||
#include "rbtree.h"
|
||||
@ -106,5 +108,7 @@ 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_stacks(struct process *process, const char *outfile);
|
||||
|
||||
void add_ignore_regex(regex_t *re);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
111
options.c
111
options.c
@ -27,6 +27,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@ -37,6 +39,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "options.h"
|
||||
@ -45,9 +49,6 @@
|
||||
#define SYSCONFDIR "/etc"
|
||||
#endif
|
||||
|
||||
#define SYSTEM_CONFIG_FILE SYSCONFDIR "/mtrace.conf"
|
||||
#define USER_CONFIG_FILE "~/.mtrace.conf"
|
||||
|
||||
#define MIN_STACK 4
|
||||
#define MAX_STACK 128
|
||||
|
||||
@ -56,6 +57,10 @@
|
||||
|
||||
struct options_t options;
|
||||
|
||||
static struct opt_F_t *opt_F_last;
|
||||
static struct opt_p_t *opt_p_last;
|
||||
static struct opt_b_t *opt_b_last;
|
||||
|
||||
static char *progname; /* Program name (`mtrace') */
|
||||
|
||||
static void err_usage(void)
|
||||
@ -150,6 +155,71 @@ static char *search_for_command(char *filename)
|
||||
return filename;
|
||||
}
|
||||
|
||||
static int add_opt_F(char *filename)
|
||||
{
|
||||
struct opt_F_t *tmp = malloc(sizeof(*tmp));
|
||||
|
||||
if (access(filename, R_OK))
|
||||
return -1;
|
||||
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tmp->filename = strdup(filename);
|
||||
tmp->next = NULL;
|
||||
|
||||
if (opt_F_last)
|
||||
opt_F_last->next = tmp;
|
||||
opt_F_last = tmp;
|
||||
|
||||
if (!options.opt_F)
|
||||
options.opt_F = tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void def_config(void)
|
||||
{
|
||||
char *path;
|
||||
char *filename;
|
||||
|
||||
path = getenv("HOME");
|
||||
if (!path) {
|
||||
struct passwd *pwd = getpwuid(getuid());
|
||||
|
||||
if (pwd != NULL)
|
||||
path = pwd->pw_dir;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
if (asprintf(&filename, "%s/.mtrace", path) != -1) {
|
||||
if (add_opt_F(filename))
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
else {
|
||||
path = getenv("XDG_CONFIG_HOME");
|
||||
if (path) {
|
||||
if (asprintf(&filename, "%s/mtrace", path) != -1) {
|
||||
if (add_opt_F(filename))
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asprintf(&filename, "%s/mtrace.conf", SYSCONFDIR) != -1) {
|
||||
if (add_opt_F(filename))
|
||||
free(filename);
|
||||
}
|
||||
else
|
||||
if (asprintf(&filename, "%s/mtrace.conf", "/etc") != -1) {
|
||||
if (add_opt_F(filename))
|
||||
free(filename);
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_int(const char *optarg, char opt, int min, int max)
|
||||
{
|
||||
char *endptr;
|
||||
@ -165,10 +235,6 @@ static int parse_int(const char *optarg, char opt, int min, int max)
|
||||
|
||||
char **process_options(int argc, char **argv)
|
||||
{
|
||||
struct opt_F_t *opt_F_last = NULL;
|
||||
struct opt_p_t *opt_p_last = NULL;
|
||||
struct opt_b_t *opt_b_last = NULL;
|
||||
|
||||
progname = argv[0];
|
||||
|
||||
options.auto_scan = 0;
|
||||
@ -282,25 +348,11 @@ char **process_options(int argc, char **argv)
|
||||
options.follow = 1;
|
||||
break;
|
||||
case 'F':
|
||||
{
|
||||
struct opt_F_t *tmp = malloc(sizeof(*tmp));
|
||||
|
||||
if (!tmp) {
|
||||
fprintf(stderr, "%s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
tmp->filename = optarg;
|
||||
tmp->next = NULL;
|
||||
|
||||
if (opt_F_last)
|
||||
opt_F_last->next = tmp;
|
||||
opt_F_last = tmp;
|
||||
|
||||
if (!options.opt_F)
|
||||
options.opt_F = tmp;
|
||||
break;
|
||||
if (add_opt_F(strdup(optarg)) == -1) {
|
||||
fprintf(stderr, "config file not found %s\n", optarg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'h':
|
||||
|
||||
usage();
|
||||
@ -465,13 +517,8 @@ char **process_options(int argc, char **argv)
|
||||
if (options.bt_depth > MAX_STACK)
|
||||
options.bt_depth = MAX_STACK;
|
||||
|
||||
if (!options.opt_F) {
|
||||
options.opt_F = malloc(sizeof(struct opt_F_t));
|
||||
options.opt_F->filename = USER_CONFIG_FILE;
|
||||
options.opt_F->next = malloc(sizeof(struct opt_F_t));
|
||||
options.opt_F->next->filename = SYSTEM_CONFIG_FILE;
|
||||
options.opt_F->next->next = NULL;
|
||||
}
|
||||
if (!options.opt_F)
|
||||
def_config();
|
||||
|
||||
if (argc > 0)
|
||||
options.command = search_for_command(argv[0]);
|
||||
|
||||
@ -181,10 +181,8 @@ int connect_to(const char *node, const char *service)
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
if (!rp) {
|
||||
fprintf(stderr, "Could not connect\n");
|
||||
if (!rp)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return sfd;
|
||||
}
|
||||
@ -224,10 +222,8 @@ int bind_to(const char *node, const char *service)
|
||||
close(sfd);
|
||||
}
|
||||
|
||||
if (!rp) {
|
||||
fprintf(stderr, "Could not bind\n");
|
||||
if (!rp)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &const_int_1, sizeof(const_int_1)))
|
||||
|
||||
4
task.c
4
task.c
@ -43,6 +43,7 @@
|
||||
#include "library.h"
|
||||
#include "mtelf.h"
|
||||
#include "report.h"
|
||||
#include "server.h"
|
||||
#include "task.h"
|
||||
#include "trace.h"
|
||||
|
||||
@ -322,7 +323,8 @@ int task_clone(struct task *task, struct task *newtask)
|
||||
if (backtrace_init(newtask) < 0)
|
||||
goto fail;
|
||||
|
||||
breakpoint_hw_clone(newtask);
|
||||
if (server_connected())
|
||||
breakpoint_hw_clone(newtask);
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user