diff --git a/CMakeLists.txt b/CMakeLists.txt index 68f4395..94f620e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.9) set(MT "mtrace-ng") -project(${MT}) +project(${MT} C) -set(MT_VERSION_STRING "0.7") +set(MT_VERSION_STRING "0.8") option(DISABLE_CLIENT "whether to disable client support" OFF) @@ -10,7 +10,7 @@ set(default_build_type "Release") if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose type of build" FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Relase" "LTO") + "Debug" "Release" "LTO") endif() include(CheckFunctionExists) @@ -45,10 +45,13 @@ include_directories( "${PROJECT_SOURCE_DIR}/sysdeps" ) -set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -Wall") -set(CMAKE_C_FLAGS_RELEASE "-O3 -Wall -DNDEBUG") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -D__FORITFY_SOURCE=2 -rdynamic") +set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined") + set(CMAKE_C_FLAGS_LTO "${CMAKE_C_FLAGS_RELEASE} -flto") -set(CMAKE_EXE_LINKER_FLAGS_LTO "-flto") +set(CMAKE_EXE_LINKER_FLAGS_LTO "${CMAKE_LINKER_FLAGS_RELEASE} -flto") + +add_compile_options(-Wall -Wextra -Werror -Werror -Wno-implicit-fallthrough) check_ipo_supported(RESULT IPO) #if (IPO) diff --git a/backend.h b/backend.h index 905d122..b33cde0 100644 --- a/backend.h +++ b/backend.h @@ -56,8 +56,8 @@ int trace_set_options(struct task *task); /* make the forked process traceable */ void trace_me(void); -/* stop tracing a task. */ -int untrace_task(struct task *task, int signum); +/* stop tracing of a task. */ +int untrace_task(struct task *task); /* Called when mtrace-ng needs to attach to task */ int trace_attach(struct task *task); diff --git a/breakpoint.c b/breakpoint.c index 910f49d..75665b0 100644 --- a/breakpoint.c +++ b/breakpoint.c @@ -70,28 +70,37 @@ static void enable_sw_breakpoint(struct task *task, struct breakpoint *bp) { static unsigned char break_insn[] = BREAKPOINT_VALUE; + assert(task->stopped); + assert(bp->sw == 0); + assert(bp->hw == 0); + debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr); copy_from_to_proc(task, bp->addr, break_insn, bp->orig_value, BREAKPOINT_LENGTH); bp->break_insn = !memcmp(break_insn, bp->orig_value, BREAKPOINT_LENGTH); bp->was_hw = 0; + bp->sw = 1; + bp->enabled = 1; + + if (bp->break_insn) + fprintf(stderr, "!!!break insn pid=%d, addr=%#lx\n", task->pid, bp->addr); } -static void disable_sw_breakpoint(struct task *task, const struct breakpoint *bp) +static void disable_sw_breakpoint(struct task *task, struct breakpoint *bp) { debug(DEBUG_PROCESS, "pid=%d, addr=%lx", task->pid, bp->addr); + assert(task->stopped); + assert(bp->sw); + assert(bp->hw == 0); + copy_to_proc(task, bp->addr, bp->orig_value, BREAKPOINT_LENGTH); + + bp->sw = 0; + bp->enabled = 0; } -int breakpoint_on_hit(struct task *task, struct breakpoint *bp) -{ - if (bp->on_hit) - return (bp->on_hit)(task, bp); - - return 0; -} struct breakpoint *breakpoint_find(struct task *task, arch_addr_t addr) { @@ -109,6 +118,7 @@ static void enable_hw_bp(struct task *task, struct breakpoint *bp) assert(bp->type != BP_SW); assert(bp->type < BP_HW || task->hw_bp[slot] == NULL); + assert(bp->sw == 0); task->hw_bp[slot] = bp; @@ -120,6 +130,8 @@ static void disable_hw_bp(struct task *task, struct breakpoint *bp) { unsigned int slot = bp->hw_bp_slot; + assert(bp->sw == 0); + if (unlikely(!task->hw_bp[slot])) return; @@ -185,13 +197,16 @@ void reorder_hw_bp(struct task *task) } } - if (!n) + if (n < HW_BREAKPOINTS - 1) { + for(i = 0; i < n; ++i) + bp_list[i]->hwcnt = 0; return; + } qsort(bp_list, n, sizeof(*bp_list), hw_bp_sort); for(i = 0; i < n; ++i) - bp_list[i]->hwcnt = (i < HW_BREAKPOINTS - 1) ? BP_REORDER_THRESHOLD >> (i + 4) : 0; + bp_list[i]->hwcnt = 0; if (n > HW_BREAKPOINTS - 1) n = HW_BREAKPOINTS - 1; @@ -205,9 +220,11 @@ void reorder_hw_bp(struct task *task) assert((hw_bp_set & 1 << bp_list[i]->hw_bp_slot) == 0); hw_bp_set |= 1 << bp_list[i]->hw_bp_slot; + + continue; } - else - *p++ = bp_list[i]; + + *p++ = bp_list[i]; } if (p == bp_list) @@ -231,6 +248,7 @@ void reorder_hw_bp(struct task *task) if (leader->hw_bp[i]) hw2sw_bp(leader, leader->hw_bp[i]); + bp->enabled = 1; bp->hw_bp_slot = i; bp->hw = 1; bp->was_hw = 1; @@ -366,6 +384,7 @@ struct breakpoint *breakpoint_new_ext(struct task *task, arch_addr_t addr, struc bp->ext = ext; bp->refcnt = 1; bp->count = 0; + bp->sw = 0; #if HW_BREAKPOINTS > 1 bp->hwcnt = 0; #endif @@ -432,7 +451,6 @@ void breakpoint_enable(struct task *task, struct breakpoint *bp) #endif bp->hw = 0; enable_sw_breakpoint(task, bp); - bp->enabled = 1; } } @@ -467,7 +485,6 @@ void breakpoint_disable(struct task *task, struct breakpoint *bp) } #endif disable_sw_breakpoint(task, bp); - bp->enabled = 0; } } @@ -529,6 +546,8 @@ void breakpoint_delete(struct task *task, struct breakpoint *bp) static int enable_nonlocked_bp_cb(unsigned long key, const void *value, void *data) { + (void)key; + struct breakpoint *bp = (struct breakpoint *)value; struct task *leader = (struct task *)data; @@ -550,6 +569,8 @@ void breakpoint_enable_all_nonlocked(struct task *leader) static int disable_nonlocked_bp_cb(unsigned long key, const void *value, void *data) { + (void)key; + struct breakpoint *bp = (struct breakpoint *)value; struct task *leader = (struct task *)data; @@ -567,7 +588,6 @@ void breakpoint_disable_all_nonlocked(struct task *leader) if (leader->breakpoints) dict_apply_to_all(leader->breakpoints, disable_nonlocked_bp_cb, leader); - leader->attached = 1; } static int enable_bp_cb(unsigned long key, const void *value, void *data) @@ -575,6 +595,8 @@ static int enable_bp_cb(unsigned long key, const void *value, void *data) struct breakpoint *bp = (struct breakpoint *)value; struct task *leader = (struct task *)data; + (void)key; + debug(DEBUG_FUNCTION, "pid=%d", leader->pid); breakpoint_enable(leader, bp); @@ -595,6 +617,8 @@ static int disable_bp_cb(unsigned long key, const void *value, void *data) struct breakpoint *bp = (struct breakpoint *)value; struct task *leader = (struct task *)data; + (void)key; + debug(DEBUG_FUNCTION, "pid=%d", leader->pid); breakpoint_disable(leader, bp); @@ -608,7 +632,30 @@ void breakpoint_disable_all(struct task *leader) if (leader->breakpoints) dict_apply_to_all(leader->breakpoints, disable_bp_cb, leader); - leader->attached = 1; +} + +static int invalidate_bp_cb(unsigned long key, const void *value, void *data) +{ + struct breakpoint *bp = (struct breakpoint *)value; + struct task *leader = (struct task *)data; + + (void)key; + + debug(DEBUG_FUNCTION, "pid=%d", leader->pid); + + bp->enabled = 0; + bp->sw = 0; + bp->hw = 0; + + return 0; +} + +void breakpoint_invalidate_all(struct task *leader) +{ + debug(DEBUG_FUNCTION, "pid=%d", leader->pid); + + if (leader->breakpoints) + dict_apply_to_all(leader->breakpoints, invalidate_bp_cb, leader); } static int destroy_breakpoint_cb(unsigned long key, const void *value, void *data) @@ -616,6 +663,8 @@ static int destroy_breakpoint_cb(unsigned long key, const void *value, void *dat struct breakpoint *bp = (struct breakpoint *)value; struct task *leader = (struct task *)data; + (void)key; + breakpoint_delete(leader, bp); return 0; } @@ -624,7 +673,9 @@ void breakpoint_clear_all(struct task *leader) { if (leader->breakpoints) { dict_apply_to_all(leader->breakpoints, &destroy_breakpoint_cb, leader); +#if HW_BREAKPOINTS > 0 assert(leader->hw_bp_num == 0); +#endif dict_clear(leader->breakpoints); leader->breakpoints = NULL; } @@ -643,6 +694,8 @@ static int clone_single_cb(unsigned long key, const void *value, void *data) struct task *new_task = (struct task *)data; size_t ext = bp->ext; + (void)key; + if (bp->deleted) return 0; @@ -665,6 +718,7 @@ static int clone_single_cb(unsigned long key, const void *value, void *data) new_bp->enabled = bp->enabled; new_bp->locked = bp->locked; new_bp->hw = bp->hw; + new_bp->sw = bp->sw; new_bp->type = bp->type; new_bp->ext = ext; new_bp->refcnt = 1; @@ -695,7 +749,8 @@ static int clone_single_cb(unsigned long key, const void *value, void *data) } else #endif - memcpy(new_bp->orig_value, bp->orig_value, sizeof(bp->orig_value)); + if (new_bp->sw) + memcpy(new_bp->orig_value, bp->orig_value, sizeof(bp->orig_value)); if (ext) memcpy((void *)new_bp + ext, (void *)bp + ext, ext); @@ -732,6 +787,8 @@ int breakpoint_put(struct breakpoint *bp) if (--bp->refcnt) return 0; + assert(bp->enabled == 0); + free(bp); } return 1; diff --git a/breakpoint.h b/breakpoint.h index b391c4a..311b7e4 100644 --- a/breakpoint.h +++ b/breakpoint.h @@ -50,6 +50,7 @@ struct breakpoint { unsigned int enabled:1; unsigned int locked:1; unsigned int deleted:1; + unsigned int sw:1; unsigned int hw:1; unsigned int was_hw:1; unsigned int break_insn:1; @@ -69,9 +70,6 @@ struct breakpoint { /* setup the basic breakpoint support for a given leader */ void breakpoint_setup(struct task *leader); -/* Call on-hit handler of BP, if any is set. */ -int breakpoint_on_hit(struct task *task, struct breakpoint *bp); - /* get a new breakpoint structure. */ struct breakpoint *breakpoint_new(struct task *task, arch_addr_t addr, struct library_symbol *libsym, int type); @@ -92,6 +90,7 @@ void breakpoint_disable(struct task *task, struct breakpoint *bp); void breakpoint_enable_all(struct task *leader); void breakpoint_disable_all(struct task *leader); +void breakpoint_invalidate_all(struct task *leader); void breakpoint_enable_all_nonlocked(struct task *leader); void breakpoint_disable_all_nonlocked(struct task *leader); void breakpoint_clear_all(struct task *leader); @@ -105,10 +104,14 @@ void disable_scratch_hw_bp(struct task *task, struct breakpoint *bp); #else static inline void enable_scratch_hw_bp(struct task *task, struct breakpoint *bp) { + (void)task; + (void)bp; } static inline void disable_scratch_hw_bp(struct task *task, struct breakpoint *bp) { + (void)task; + (void)bp; } #endif @@ -120,10 +123,12 @@ void breakpoint_hw_destroy(struct task *task); #else static inline void breakpoint_hw_clone(struct task *task) { + (void)task; } static inline void breakpoint_hw_destroy(struct task *task) { + (void)task; } #endif diff --git a/client/binfile.c b/client/binfile.c index b1536fe..8a8b780 100644 --- a/client/binfile.c +++ b/client/binfile.c @@ -151,7 +151,7 @@ struct rb_sym *bin_file_lookup(struct bin_file *binfile, bfd_vma addr, unsigned if (!name || !*name) name = "?"; else { - alloc = bfd_demangle(binfile->abfd, name, DMGL_TYPES | DMGL_VERBOSE | DMGL_ANSI | DMGL_PARAMS); + alloc = bfd_demangle(binfile->abfd, name, DMGL_ANSI | DMGL_PARAMS | DMGL_RET_DROP | DMGL_AUTO); if (alloc) name = alloc; } @@ -289,10 +289,9 @@ void bin_file_sym_put(struct rb_sym *sym) if (!--sym->refcnt) { free(sym->sym); - if (!binfile) - return; - - rb_erase(&sym->node, &binfile->sym_table); + if (binfile) + rb_erase(&sym->node, &binfile->sym_table); + free(sym); } bin_file_put(binfile); } diff --git a/client/client.c b/client/client.c index 308e526..e39313e 100644 --- a/client/client.c +++ b/client/client.c @@ -364,8 +364,10 @@ static void client_remove_process(struct process *process) { process = pid_rb_delete(&pid_table, process->pid); - if (process) + if (process) { + process_reset(process); free(process); + } } @@ -487,14 +489,15 @@ static int client_func(void) process_about_exit(process); break; case MT_EXIT: - process_exit(process); + if (process_exit(process)) + client_remove_process(process); break; case MT_NOFOLLOW: - process_reset(process); client_remove_process(process); break; case MT_SCAN: - process_scan(process, payload, mt_msg.payload_len); + if (process_scan(process, payload, mt_msg.payload_len)) + client_remove_process(process); break; case MT_ADD_MAP: process_add_map(process, payload, mt_msg.payload_len); @@ -503,7 +506,8 @@ static int client_func(void) process_del_map(process, payload, mt_msg.payload_len); break; case MT_DETACH: - process_detach(process); + if (process_detach(process)) + client_remove_process(process); break; default: fatal("protocol violation 0x%08x", mt_msg.operation); @@ -659,8 +663,7 @@ static void signal_exit(int sig) signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); - if (write(pipefd[1], &signum, 1) == -1) - ; + write(pipefd[1], &signum, 1); } static int scan_process(struct process *process) @@ -761,6 +764,8 @@ int client_start(void) void *client_thread(void *unused) { + (void)unused; + if (options.interactive) { ioevent_add_input(client_fd, client_func); @@ -824,6 +829,7 @@ int client_stop(void) { if (thread) { thread_join(thread); + free(thread); thread = NULL; } diff --git a/client/process.c b/client/process.c index 7cf985b..bc969d8 100644 --- a/client/process.c +++ b/client/process.c @@ -424,6 +424,7 @@ static void stack_put(struct rb_stack *stack_node) for(i = 0; i < stack->entries; ++i) bin_file_sym_put(stack->syms[i]); + free(stack->addrs); free(stack->syms); } free(stack); @@ -479,7 +480,7 @@ static struct rb_stack *stack_clone(struct process *process, struct rb_stack *st return this; } -static struct rb_stack *stack_add(struct process *process, unsigned int pid, void *addrs, uint32_t stack_size, enum mt_operation operation) +static struct rb_stack *stack_add(struct process *process, void *addrs, uint32_t stack_size, enum mt_operation operation) { struct rb_root *root = &process->stack_table; struct rb_node **new = &(root->rb_node), *parent = NULL; @@ -775,6 +776,8 @@ void process_del_map(struct process *process, void *payload, uint32_t payload_le uint64_t size = process->val64(mt_map->size); struct list_head *it; + (void)payload_len; + list_for_each(it, &process->map_list) { struct map *map = container_of(it, struct map, list); @@ -1144,6 +1147,8 @@ static void process_dump(struct process *process, int (*sortby)(const struct rb_ static int skip_none(struct rb_stack *stack) { + (void)stack; + return 0; } @@ -1207,7 +1212,7 @@ void process_dump_stacks(struct process *process, const char *outfile, int lflag process_dump(process, sort_allocations, skip_none, outfile, lflag); } -void *process_scan(struct process *process, void *leaks, uint32_t payload_len) +int process_scan(struct process *process, void *leaks, uint32_t payload_len) { unsigned int new = 0; unsigned long n = payload_len / process->ptr_size; @@ -1250,10 +1255,12 @@ void *process_scan(struct process *process, void *leaks, uint32_t payload_len) dump_printf("leaks total: %lu\n", process->leaks); dump_flush(); - if (!options.interactive) + if (!options.interactive) { process_dump_sortby(process); + return 1; + } - return leaks; + return 0; } static inline unsigned long roundup_mask(unsigned long val, unsigned long mask) @@ -1430,7 +1437,7 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) if (stack_size) { if (!is_sane(block, mt_msg->operation)) { - struct rb_stack *stack = stack_add(process, process->pid, stack_data, stack_size, mt_msg->operation); + struct rb_stack *stack = stack_add(process, stack_data, stack_size, mt_msg->operation); stack->n_mismatched++; stack->tsc = process->tsc++; @@ -1462,7 +1469,7 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload) } if (stack_size) { - struct rb_stack *stack = stack_add(process, process->pid, stack_data, stack_size, mt_msg->operation); + struct rb_stack *stack = stack_add(process, stack_data, stack_size, mt_msg->operation); stack->n_badfree++; stack->tsc = process->tsc++; @@ -1477,6 +1484,8 @@ void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void * unsigned int pid; struct list_head *it; + (void)mt_msg; + if (!process->tracing) return; @@ -1508,7 +1517,7 @@ void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void * } if (unlikely(options.kill)) { - fprintf(stderr, ">>> unexpected realloc done pid: %u\n", pid); + fprintf(stderr, ">>> unexpected realloc done pid: %u ptr: %#lx\n", pid, ptr); abort(); } return; @@ -1562,7 +1571,7 @@ void process_alloc(struct process *process, struct mt_msg *mt_msg, void *payload process_rb_delete_block(process, block); } - struct rb_stack *stack = stack_add(process, process->pid, stack_data, stack_size, mt_msg->operation); + struct rb_stack *stack = stack_add(process, stack_data, stack_size, mt_msg->operation); if (process_rb_insert_block(process, ptr, size, stack, 0, mt_msg->operation)) { fprintf(stderr, "process_rb_insert_block failed\n"); @@ -1636,14 +1645,17 @@ void process_dump_sortby(struct process *process) } } -void process_exit(struct process *process) +int process_exit(struct process *process) { process_set_status(process, MT_PROCESS_EXIT); - if (!options.interactive) + if (!options.interactive) { process_dump_sortby(process); - else - fprintf(stderr, "+++ process %d exited +++\n", process->pid); + return 1; + } + + fprintf(stderr, "+++ process %d exited\n", process->pid); + return 0; } void process_about_exit(struct process *process) @@ -1656,17 +1668,25 @@ void process_about_exit(struct process *process) client_send_msg(process, MT_ABOUT_EXIT, NULL, 0); } -void process_detach(struct process *process) +int process_detach(struct process *process) { + int ret = 0; + process_set_status(process, MT_PROCESS_DETACH); - if (options.auto_scan) + if (options.auto_scan) { process_leaks_scan(process, SCAN_ALL); - else - if (!options.interactive) - process_dump_sortby(process); + } + else { + if (!options.interactive) { + process_dump_sortby(process); + ret = 1; + } + } client_send_msg(process, MT_DETACH, NULL, 0); + + return ret; } void process_set_status(struct process *process, enum process_status status) diff --git a/client/process.h b/client/process.h index 16c1206..0bafae9 100644 --- a/client/process.h +++ b/client/process.h @@ -90,15 +90,15 @@ void process_set_status(struct process *process, enum process_status status); void process_start_input(struct process *process); void process_stop_input(struct process *process); void process_about_exit(struct process *process); -void process_exit(struct process *process); +int process_exit(struct process *process); void process_status(struct process *process); -void *process_scan(struct process *curr, void *leaks, uint32_t payload_len); +int process_scan(struct process *curr, void *leaks, uint32_t payload_len); void process_alloc(struct process *process, struct mt_msg *msg, void *payload); void process_free(struct process *process, struct mt_msg *msg, void *payload); void process_munmap(struct process *process, struct mt_msg *msg, void *payload); void process_add_map(struct process *process, void *payload, uint32_t payload_len); void process_del_map(struct process *process, void *payload, uint32_t payload_len); -void process_detach(struct process *process); +int process_detach(struct process *process); void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void *payload); unsigned long process_leaks_scan(struct process *process, int mode); diff --git a/client/readline.c b/client/readline.c index c8f79d3..a8972af 100644 --- a/client/readline.c +++ b/client/readline.c @@ -90,35 +90,35 @@ static const char *outfile; static int quit; static struct cmd_opt dump_opts[] = { - { "allocations", 2, process_dump_sort_allocations, "sort by number of open allocations" }, - { "average", 2, process_dump_sort_average, "sort by average allocation of bytes (usage / allocations)" }, - { "badfree", 1, process_dump_sort_badfree, "sort by number of badfree releases" }, - { "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" }, - { "usage", 1, process_dump_sort_usage, "sort by number of bytes" }, - { NULL, 0, NULL, "\n use > to dump the output into a file" }, + { "allocations", 2, process_dump_sort_allocations, "sort by number of open allocations", NULL, NULL }, + { "average", 2, process_dump_sort_average, "sort by average allocation of bytes (usage / allocations)", NULL, NULL }, + { "badfree", 1, process_dump_sort_badfree, "sort by number of badfree releases", NULL, NULL }, + { "bytes-leaked", 1, process_dump_sort_bytes_leaked, "sort by number of leaked bytes", NULL, NULL }, + { "leaks", 1, process_dump_sort_leaks, "sort by number of detected leaks", NULL, NULL }, + { "mismatched", 1, process_dump_sort_mismatched, "sort by number of mismatched releases", NULL, NULL }, + { "stacks", 1, process_dump_stacks, "dump all stack sort by number of total allocations", NULL, NULL }, + { "total", 2, process_dump_sort_total, "sort by number of total allocations", NULL, NULL }, + { "tsc", 2, process_dump_sort_tsc, "sort by time stamp counter", NULL, NULL }, + { "usage", 1, process_dump_sort_usage, "sort by number of bytes", NULL, NULL }, + { NULL, 0, NULL, "\n use > to dump the output into a file", NULL, NULL }, }; static struct cmd_opt set_opts[] = { - { "searchpath", 1, do_set_searchpath, "set searchpath for binaries and libraries" }, - { "depth", 1, do_set_depth, "set backtrace depth" }, + { "searchpath", 1, do_set_searchpath, "set searchpath for binaries and libraries", NULL, NULL }, + { "depth", 1, do_set_depth, "set backtrace depth", NULL, NULL }, { }, }; static struct cmd_opt show_opts[] = { - { "info", 1, do_show_info, "show client settings" }, - { "searchpath", 1, do_show_searchpath, "show searchpath for binaries and libraries" }, + { "info", 1, do_show_info, "show client settings", NULL, NULL }, + { "searchpath", 1, do_show_searchpath, "show searchpath for binaries and libraries", NULL, NULL }, { }, }; static struct cmd_opt scan_opts[] = { - { "all", 1, (void *)SCAN_ALL, "scan all memory blocks" }, - { "leak", 1, (void *)SCAN_LEAK, "scan only leaked allocations" }, - { "new", 1, (void *)SCAN_NEW, "scan only allocations since last scan" }, + { "all", 1, (void *)SCAN_ALL, "scan all memory blocks", NULL, NULL }, + { "leak", 1, (void *)SCAN_LEAK, "scan only leaked allocations", NULL, NULL }, + { "new", 1, (void *)SCAN_NEW, "scan only allocations since last scan", NULL, NULL }, { }, }; @@ -144,21 +144,24 @@ static struct cmd_opt cmds[] = { 1, do_proclist, "list processes", - "" + "", + NULL }, { quit_str, 1, do_quit, "exit the program", - "" + "", + NULL }, { reset_str, 1, do_reset, "reset all current memory allocation", - "[]" + "[]", + NULL }, { scan_str, @@ -189,21 +192,24 @@ static struct cmd_opt cmds[] = { 4, do_start, "start allocation tracing", - "" + "", + NULL }, { status_str, 4, do_status, "show allocation status", - "[]" + "[]", + NULL }, { stop_str, 3, do_stop, "stop allocation tracing", - "" + "", + NULL }, { }, }; @@ -513,6 +519,8 @@ static int do_set_depth(struct cmd_opt *cmd, struct cmd_opt *opt, int argc, cons static int do_show_info(struct cmd_opt *cmd, struct cmd_opt *opt, int argc, const char *argv[]) { + (void)argv; + if (argc > 2) { fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name); return -1; @@ -528,6 +536,8 @@ static int do_show_searchpath(struct cmd_opt *cmd, struct cmd_opt *opt, int argc { struct opt_b_t *p = options.opt_b; + (void)argv; + if (argc > 3) { fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name); return -1; @@ -661,6 +671,9 @@ static int show_process(struct process *process) static int do_proclist(struct cmd_opt *cmd, int argc, const char *argv[]) { + (void)cmd; + (void)argv; + if (argc > 1) { fprintf(stderr, "%s: expect no arguments\n", proclist_str); return -1; @@ -674,6 +687,10 @@ static int do_proclist(struct cmd_opt *cmd, int argc, const char *argv[]) static int do_quit(struct cmd_opt *cmd, int argc, const char *argv[]) { + (void)cmd; + (void)argc; + (void)argv; + quit = 1; return 0; @@ -683,6 +700,9 @@ static int do_reset(struct cmd_opt *cmd, int argc, const char *argv[]) { struct process *process; + (void)cmd; + (void)argc; + process = get_process(argv[1]); if (!process) return -1; @@ -702,6 +722,8 @@ static int do_scan(struct cmd_opt *cmd, int argc, const char *argv[]) unsigned int i; int mode; + (void)argc; + if (!client_connected()) return -1; @@ -798,6 +820,9 @@ static int do_start(struct cmd_opt *cmd, int argc, const char *argv[]) { struct process *process; + (void)cmd; + (void)argc; + if (!client_connected()) return -1; @@ -819,6 +844,9 @@ static int do_status(struct cmd_opt *cmd, int argc, const char *argv[]) { struct process *process; + (void)cmd; + (void)argc; + process = get_process(argv[1]); if (!process) return -1; @@ -832,6 +860,9 @@ static int do_stop(struct cmd_opt *cmd, int argc, const char *argv[]) { struct process *process; + (void)cmd; + (void)argc; + if (!client_connected()) return -1; diff --git a/config.h.in b/config.h.in index 1e5b393..6120ed6 100644 --- a/config.h.in +++ b/config.h.in @@ -1,6 +1,13 @@ #define PACKAGE_VERSION "@MT_VERSION_STRING@" +#ifndef HAVE_PROCESS_VM_READV #cmakedefine HAVE_PROCESS_VM_READV -#cmakedefine HAVE_LIBSELINUX +#endif +#ifndef HAVE_LIBSELINUX +#cmakedefine HAVE_LIBSELINUX +#endif + +#ifdef DISABLE_CLIENT #cmakedefine DISABLE_CLIENT +#endif diff --git a/debug.c b/debug.c index d89d05a..b8c34db 100644 --- a/debug.c +++ b/debug.c @@ -47,6 +47,9 @@ void _debug(int level, const char *file, const char *function, int line, const c va_end(args); fprintf(stderr, "DEBUG: %s():%s@%d - %s\n", function, file, line, buf); + +// fflush(debug_file); + free(buf); } #endif diff --git a/debug.h b/debug.h index b243e53..6b95556 100644 --- a/debug.h +++ b/debug.h @@ -26,6 +26,8 @@ #include "config.h" +#define DEBUG + /* debug levels: */ enum { diff --git a/dwarf.c b/dwarf.c index 8cdd776..91401e1 100644 --- a/dwarf.c +++ b/dwarf.c @@ -1166,7 +1166,7 @@ static int run_cfi_program(struct dwarf_addr_space *as, struct dwarf_reg_state * set_reg(rs_current, regnum, DWARF_WHERE_REG, n); break; case DW_CFA_remember_state: - rs_tmp = malloc(regs_size(as)); + rs_tmp = malloc(sizeof(struct dwarf_reg_stack) + regs_size(as)); memcpy(&rs_tmp->store, rs_current, regs_size(as)); rs_tmp->next = rs_stack; rs_stack = rs_tmp; diff --git a/event.c b/event.c index 3bd9131..abc8c12 100644 --- a/event.c +++ b/event.c @@ -53,10 +53,16 @@ #include "timer.h" #include "trace.h" +#define RET_DELETED 1 +#define RET_DEFERED 2 + static LIST_HEAD(event_head); void queue_event(struct task *task) { + assert(task->event.type != EVENT_NONE); + assert(task->stopped); + if (task) { if (task->event.type != EVENT_NONE) list_add_tail(&task->event.list, &event_head); @@ -88,52 +94,47 @@ void init_event(struct task *task) INIT_LIST_HEAD(&task->event.list); } -static void show_clone(struct task *task, enum event_type type) +static const char * get_clone_type(enum event_type type) { - const char *str; - switch(type) { case EVENT_FORK: - str = "fork"; - break; + return "fork"; case EVENT_VFORK: - str = "vfork"; - break; + return "vfork"; case EVENT_CLONE: - str = "clone"; - break; + return "clone"; default: - str = "?"; break; } - fprintf(stderr, "+++ process pid=%d %s (newpid=%d) +++\n", task->pid, str, task->event.e_un.newpid); + return "?"; } -static void handle_clone(struct task *task, enum event_type type) +static int do_clone(struct task *task, struct task *newtask) { - struct task *newtask; - int newpid = task->event.e_un.newpid; - - debug(DEBUG_FUNCTION, "pid=%d, newpid=%d", task->pid, newpid); + debug(DEBUG_EVENT, "+++ process %s pid=%d, newpid=%d", get_clone_type(task->event.type), task->pid, newtask->pid); if (unlikely(options.verbose)) - show_clone(task, type); + fprintf(stderr, "+++ process %s(%d) pid=%d, newpid=%d\n", get_clone_type(task->event.type), task->event.type, task->pid, newtask->pid); - continue_task(task, 0); + assert(task->stopped); + assert(newtask->stopped); + assert(newtask->is_new); - newtask = pid2task(newpid); - if (!newtask) - goto fail; + if (newtask->event.type != EVENT_NEW) + fprintf(stderr, "!!!task new unexpected event for pid=%d: %d\n", newtask->pid, newtask->event.type); + else + if (newtask->event.e_un.signum) + fprintf(stderr, "!!!task new unexpected signal for pid=%d: %d\n", newtask->pid, newtask->event.e_un.signum); if (newtask->leader == newtask) { + if (!options.follow) { + untrace_proc(newtask); + return RET_DELETED; + } + if (task_fork(task, newtask) < 0) goto fail; - if (!options.follow) { - remove_proc(newtask); - return; - } - report_fork(newtask, task); } else { @@ -141,77 +142,130 @@ static void handle_clone(struct task *task, enum event_type type) goto fail; } - continue_task(newtask, newtask->event.e_un.signum); - - return; + newtask->is_new = 0; + return continue_task(newtask, 0); fail: - fprintf(stderr, - "Error during init of tracing process %d\n" - "This process won't be traced.\n", - newpid - ); + fprintf(stderr, "Error during clone of pid=%d - This process won't be traced!\n", newtask->pid); + return -1; } -static void handle_signal(struct task *task) +static int do_clone_cb(struct task *newtask, void *data) { - if (unlikely(options.verbose > 1)) { - if (task->event.e_un.signum && (task->event.e_un.signum != SIGSTOP || !task->was_stopped)) - fprintf(stderr, "+++ process pid=%d signal %d: %s +++\n", task->pid, task->event.e_un.signum, strsignal(task->event.e_un.signum)); + int ret; + struct task *task = data; + + debug(DEBUG_EVENT, "+++ process do clone cb pid=%d, newpid=%d", task->pid, newtask->pid); + + ret = do_clone(task, newtask); + continue_task(task, 0); + return ret; +} + +static int handle_child(struct task *task) +{ + struct task *newtask; + int newpid = task->event.e_un.newpid; + + debug(DEBUG_EVENT, "+++ process child pid=%d, newpid=%d", task->pid, newpid); + + newtask = pid2task(newpid); + + assert(newtask != NULL); + + if (!newtask->stopped) { + debug(DEBUG_EVENT, "+++ process defer child pid=%d, newpid=%d", task->pid, newpid); + newtask->defer_func = do_clone_cb; + newtask->defer_data = task; + return RET_DEFERED; } - continue_task(task, task->event.e_un.signum); + do_clone(task, newtask); + return continue_task(task, 0); +} + +static int handle_signal(struct task *task) +{ + debug(DEBUG_EVENT, "+++ process signal pid=%d, event signal %d", task->pid, task->event.e_un.signum); + + if (unlikely(options.verbose > 1)) { + if (task->event.e_un.signum) + fprintf(stderr, "+++ process pid=%d signal %d: %s\n", task->pid, task->event.e_un.signum, strsignal(task->event.e_un.signum)); + } + + return continue_task(task, task->event.e_un.signum); } static void show_exit(struct task *task) { if (unlikely(options.verbose)) - fprintf(stderr, "+++ process pid=%d exited (status=%d) +++\n", task->pid, task->event.e_un.ret_val); + fprintf(stderr, "+++ process pid=%d exited (status=%d)\n", task->pid, task->event.e_un.ret_val); } -static void handle_about_exit(struct task *task) +static int handle_new(struct task *task) { + debug(DEBUG_EVENT, "+++ process new pid=%d, event signal %d", task->pid, task->event.e_un.signum); + + assert(task->is_new); + + if (task->event.e_un.signum) + fprintf(stderr, "!!!task unexpected signal for pid=%d: %d\n", task->pid, task->event.e_un.signum); + task->is_new = 0; + + return continue_task(task, task->event.e_un.signum); +} + +static int handle_about_exit(struct task *task) +{ + debug(DEBUG_EVENT, "+++ process pid=%d about exit", task->pid); + if (task->leader == task) { if (!options.logfile && report_about_exit(task) != -1) { task->about_exit = 1; - return; + return 0; } } - continue_task(task, 0); + return continue_task(task, 0); } -static void handle_exit(struct task *task) +static int handle_exit(struct task *task) { + debug(DEBUG_EVENT, "+++ process pid=%d exited (status=%d)", task->pid, task->event.e_un.ret_val); + show_exit(task); if (task->leader == task) { report_exit(task); - remove_proc(task); + untrace_proc(task); } else { remove_task(task); } + return RET_DELETED; } -static void handle_exit_signal(struct task *task) +static int handle_exit_signal(struct task *task) { + debug(DEBUG_EVENT, "+++ process pid=%d killed by signal %s (%d)", task->pid, strsignal(task->event.e_un.signum), task->event.e_un.signum); + if (unlikely(options.verbose)) - fprintf(stderr, "+++ process pid=%d killed by signal %s (%d) +++\n", task->pid, strsignal(task->event.e_un.signum), task->event.e_un.signum); + fprintf(stderr, "+++ process pid=%d killed by signal %s (%d)\n", task->pid, strsignal(task->event.e_un.signum), task->event.e_un.signum); if (task->leader == task) { report_exit(task); - remove_proc(task); + untrace_proc(task); } else { remove_task(task); } + return RET_DELETED; } -static void handle_exec(struct task *task) +static int handle_exec(struct task *task) { - debug(DEBUG_FUNCTION, "pid=%d", task->pid); + debug(DEBUG_EVENT, "+++ process pid=%d exec", task->pid); if (unlikely(options.verbose)) - fprintf(stderr, "+++ process pid=%d exec (%s) +++\n", task->pid, library_execname(task)); + fprintf(stderr, "+++ process pid=%d exec\n", task->pid); if (!options.follow_exec) goto nofollow; @@ -221,18 +275,20 @@ static void handle_exec(struct task *task) goto untrace; } - continue_task(task, 0); - return; + return continue_task(task, 0); nofollow: report_nofollow(task); untrace: - remove_proc(task); + untrace_proc(task); + return RET_DELETED; } static int handle_call_after(struct task *task, struct breakpoint *bp) { struct timespec start; + (void)bp; + if (!task->breakpoint) return 0; @@ -255,16 +311,41 @@ static int handle_call_after(struct task *task, struct breakpoint *bp) return 0; } -static void handle_breakpoint(struct task *task) +static int handle_breakpoint(struct task *task) { struct breakpoint *bp = task->event.e_un.breakpoint; unsigned int hw = bp->hw; - debug(DEBUG_FUNCTION, "pid=%d, addr=%#lx", task->pid, bp->addr); + debug(DEBUG_EVENT, "+++ process pid=%d breakpoint addr=%#lx", task->pid, bp->addr); + + assert(task->stopped); if (unlikely(options.verbose > 1)) set_timer(&task->halt_time, hw ? &hw_bp_time : &sw_bp_time); + if (unlikely(options.verbose)) + ++bp->count; + + if (unlikely(task->skip_bp)) { + struct breakpoint *skip_bp = task->skip_bp; + + task->skip_bp = NULL; + + breakpoint_put(skip_bp); + + if (likely(skip_bp == bp)) { + skip_breakpoint(task, bp); + goto end; + } + + fprintf(stderr, "%s:%d\n", __func__, __LINE__); + } + + if (unlikely(bp->deleted)) { + continue_task(task, 0); + goto end; + } + #if HW_BREAKPOINTS > 1 if (bp->type >= BP_HW) { if (unlikely(++bp->hwcnt >= (BP_REORDER_THRESHOLD << hw))) { @@ -281,28 +362,7 @@ static void handle_breakpoint(struct task *task) } #endif - if (unlikely(options.verbose)) - ++bp->count; - - if (unlikely(task->skip_bp)) { - struct breakpoint *skip_bp = task->skip_bp; - - task->skip_bp = NULL; - - breakpoint_put(skip_bp); - - if (likely(skip_bp == bp)) { - skip_breakpoint(task, bp); - goto end; - } - } - - if (unlikely(bp->deleted)) { - continue_task(task, 0); - goto end; - } - - if (unlikely(breakpoint_on_hit(task, bp))) { + if (bp->on_hit && bp->on_hit(task, bp)) { continue_task(task, 0); goto end; } @@ -336,65 +396,79 @@ static void handle_breakpoint(struct task *task) } } - if (task->stopped) - skip_breakpoint(task, bp); - + skip_breakpoint(task, bp); end: breakpoint_put(bp); + return 0; } -int handle_event(void) +int handle_event(struct task *task) { - struct task *task = next_event(); + int ret; if (!task) return 0; + debug(DEBUG_EVENT, "+++ process pid=%d event: %d", task->pid, task->event.type); + + assert(task->stopped); + + if (task->defer_func) { + ret = task->defer_func(task, task->defer_data); + + task->defer_func = NULL; + task->defer_data = NULL; + + return ret; + } + struct event *event = &task->event; enum event_type type = event->type; - event->type = EVENT_NONE; - debug(DEBUG_FUNCTION, "pid=%d, type=%d", task->pid, event->type); - switch (type) { case EVENT_NONE: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event none", task->pid); + ret = continue_task(task, task->event.e_un.signum); break; case EVENT_SIGNAL: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event signal %d", task->pid, event->e_un.signum); - handle_signal(task); + ret = handle_signal(task); break; case EVENT_ABOUT_EXIT: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit %d", task->pid, event->e_un.ret_val); - handle_about_exit(task); - break; + ret = handle_about_exit(task); + goto out; case EVENT_EXIT: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit %d", task->pid, event->e_un.ret_val); - handle_exit(task); + ret = handle_exit(task); break; case EVENT_EXIT_SIGNAL: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit signal %d", task->pid, event->e_un.signum); - handle_exit_signal(task); + ret = handle_exit_signal(task); break; case EVENT_FORK: case EVENT_VFORK: case EVENT_CLONE: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event clone (%u)", task->pid, event->e_un.newpid); - handle_clone(task, type); + ret = handle_child(task); break; case EVENT_EXEC: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event exec()", task->pid); - handle_exec(task); + ret = handle_exec(task); break; case EVENT_BREAKPOINT: - debug(DEBUG_EVENT_HANDLER, "pid=%d, event breakpoint %#lx", task->pid, event->e_un.breakpoint->addr); - handle_breakpoint(task); + ret = handle_breakpoint(task); + goto out; + case EVENT_NEW: + ret = handle_new(task); break; default: - fprintf(stderr, "Error! unknown event?\n"); - return -1; + fprintf(stderr, "!!!pid=%d, unknown event %d\n", task->pid, type); + abort(); } - return 1; + if (ret == RET_DELETED) + return 1; + + if (ret != RET_DEFERED) { + assert(task->event.type == EVENT_NONE); + assert(task->stopped == 0); + } + assert(task->is_new == 0); +out: + return (ret < 0) ? ret : 0; } diff --git a/event.h b/event.h index f7b2ef4..51230ab 100644 --- a/event.h +++ b/event.h @@ -36,7 +36,8 @@ enum event_type { EVENT_CLONE, EVENT_VFORK, EVENT_EXEC, - EVENT_BREAKPOINT + EVENT_BREAKPOINT, + EVENT_NEW, }; struct event { @@ -54,7 +55,7 @@ void init_event(struct task *task); void remove_event(struct task *task); struct task *next_event(void); void queue_event(struct task *task); -int handle_event(void); +int handle_event(struct task *task); #endif diff --git a/library.c b/library.c index 720651a..9b30cb8 100644 --- a/library.c +++ b/library.c @@ -171,10 +171,10 @@ void library_delete_list(struct task *leader, struct list_head *list) struct library *lib = container_of(it, struct library, list); struct libref *libref = lib->libref; - debug(DEBUG_FUNCTION, "%s@%#lx", libref->filename, libref->base); + debug(DEBUG_FUNCTION, "%s@%#lx pid=%d ", libref->filename, libref->base, leader->pid); if (unlikely(options.verbose > 1)) - fprintf(stderr, "+++ library del pid=%d %s@%#lx %#lx-%#lx +++\n", leader->pid, libref->filename, libref->base, libref->load_addr, libref->load_addr + libref->load_size); + fprintf(stderr, "+++ library del pid=%d %s@%#lx %#lx-%#lx\n", leader->pid, libref->filename, libref->base, libref->load_addr, libref->load_addr + libref->load_size); library_delete(leader, lib); } @@ -274,7 +274,7 @@ static struct library *_library_add(struct task *leader, struct libref *libref) insert_lib(leader, lib); if (unlikely(options.verbose > 1)) - fprintf(stderr, "+++ library add pid=%d %s@%#lx %#lx-%#lx +++\n", leader->pid, libref->filename, libref->base, libref->load_addr, libref->load_addr + libref->load_size); + fprintf(stderr, "+++ library add pid=%d %s@%#lx %#lx-%#lx\n", leader->pid, libref->filename, libref->base, libref->load_addr, libref->load_addr + libref->load_size); return lib; } diff --git a/list.h b/list.h index ff64ef1..fd55551 100644 --- a/list.h +++ b/list.h @@ -55,41 +55,39 @@ static inline void INIT_LIST_HEAD(struct list_head *list) * This is only for internal list manipulation where we know * the prev/next entries already! */ -static inline void __list_add(struct list_head *new, - struct list_head *prev, - struct list_head *next) +static inline void __list_add(struct list_head *elem, struct list_head *prev, struct list_head *next) { - next->prev = new; - new->next = next; - new->prev = prev; - prev->next = new; + next->prev = elem; + elem->next = next; + elem->prev = prev; + prev->next = elem; } /** * list_add - add a new entry - * @new: new entry to be added + * @elem: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static inline void list_add(struct list_head *new, struct list_head *head) +static inline void list_add(struct list_head *elem, struct list_head *head) { - __list_add(new, head, head->next); + __list_add(elem, head, head->next); } /** * list_add_tail - add a new entry - * @new: new entry to be added + * @elem: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static inline void list_add_tail(struct list_head *new, struct list_head *head) +static inline void list_add_tail(struct list_head *elem, struct list_head *head) { - __list_add(new, head->prev, head); + __list_add(elem, head->prev, head); } /* diff --git a/main.c b/main.c index 0543b8e..8d82abe 100644 --- a/main.c +++ b/main.c @@ -61,6 +61,8 @@ struct mt_timer report_in_time; struct mt_timer report_out_time; struct mt_timer skip_bp_time; +pid_t mtrace_pid; + static int do_exit; void mtrace_request_exit(void) @@ -69,7 +71,7 @@ void mtrace_request_exit(void) return; if (unlikely(options.verbose)) - fprintf(stderr, "+++ request exit +++\n"); + fprintf(stderr, "+++ request exit\n"); do_exit = 1; wait_event_wakeup(); @@ -110,6 +112,8 @@ static void mtrace_init(char **cmd_args) { struct opt_p_t *opt_p_tmp; + mtrace_pid = getpid(); + if (options.command) { struct task *task = task_create(cmd_args); @@ -117,7 +121,7 @@ static void mtrace_init(char **cmd_args) exit(EXIT_FAILURE); if (unlikely(options.verbose)) - fprintf(stderr, "+++ process pid=%d created (%s) +++\n", task->pid, library_execname(task)); + fprintf(stderr, "+++ process pid=%d created (%s)\n", task->pid, library_execname(task)); } for(opt_p_tmp = options.opt_p; opt_p_tmp; opt_p_tmp = opt_p_tmp->next) @@ -126,12 +130,17 @@ static void mtrace_init(char **cmd_args) static void mtrace_main(void) { + struct task *task; + while(!do_exit) { if (task_list_empty()) break; - if (handle_event() == -1) - break; + task = next_event(); + if (task) { + if (handle_event(task) == -1) + break; + } if (server_poll() == -1) break; @@ -142,8 +151,6 @@ int main(int argc, char *argv[]) { char **cmd_args = process_options(argc, argv); - init_pid_hash(); - if (options.trace) { if (options.logfile) { if (server_logfile() == -1) diff --git a/main.h b/main.h index b3f3aaf..1e04304 100644 --- a/main.h +++ b/main.h @@ -23,6 +23,8 @@ #ifndef _INC_MAIN_H #define _INC_MAIN_H +#include + #include "timer.h" void mtrace_request_exit(void); @@ -36,5 +38,6 @@ struct mt_timer report_in_time; struct mt_timer report_out_time; struct mt_timer skip_bp_time; +pid_t mtrace_pid; #endif diff --git a/mtelf.c b/mtelf.c index 5af6f7e..190d9dd 100644 --- a/mtelf.c +++ b/mtelf.c @@ -259,7 +259,7 @@ static int elf_lib_init(struct mt_elf *mte, struct task *task, struct libref *li } #endif - if (mte->eh_hdr.p_filesz && mte->dyn_addr) { + if (mte->eh_hdr.p_filesz && mte->dyn) { if (dwarf_get_unwind_table(task, libref, (struct dwarf_eh_frame_hdr *)(libref->image_addr - libref->load_offset + mte->eh_hdr.p_offset)) < 0) return -1; } @@ -474,7 +474,7 @@ static int entry_breakpoint_on_hit(struct task *task, struct breakpoint *a) return 1; } -struct libref *elf_read_main_binary(struct task *task) +struct libref *elf_read_main_binary(struct task *task, int was_attached) { char fname[PATH_MAX]; int ret; @@ -520,7 +520,7 @@ struct libref *elf_read_main_binary(struct task *task) close_elf(&mte); - report_attach(task); + report_attach(task, was_attached); library_add(task, libref); @@ -529,7 +529,10 @@ struct libref *elf_read_main_binary(struct task *task) struct mt_elf mte_ld = { }; - copy_str_from_proc(task, ARCH_ADDR_T(mte.interp), fname, sizeof(fname)); + if (copy_str_from_proc(task, ARCH_ADDR_T(mte.bias + mte.interp), fname, sizeof(fname)) == -1) { + fprintf(stderr, "fatal error: cannot get loader name for pid=%d\n", task->pid); + abort(); + } if (!elf_read(&mte_ld, task, fname, (GElf_Addr)base)) { struct libref *libref; @@ -547,7 +550,7 @@ struct libref *elf_read_main_binary(struct task *task) if (!ret) { library_add(task, libref); - if (linkmap_init(task, ARCH_ADDR_T(mte.dyn_addr))) { + if (linkmap_init(task, ARCH_ADDR_T(mte.bias + mte.dyn))) { arch_addr_t addr = find_solib_break(&mte_ld); if (!addr) addr = ARCH_ADDR_T(entry); @@ -562,7 +565,7 @@ struct libref *elf_read_main_binary(struct task *task) else { entry_bp->breakpoint.on_hit = entry_breakpoint_on_hit; entry_bp->breakpoint.locked = 1; - entry_bp->dyn_addr = ARCH_ADDR_T(mte.dyn_addr); + entry_bp->dyn_addr = ARCH_ADDR_T(mte.bias + mte.dyn); breakpoint_enable(task, &entry_bp->breakpoint); } diff --git a/mtelf.h b/mtelf.h index 4030f44..7d34a8f 100644 --- a/mtelf.h +++ b/mtelf.h @@ -53,15 +53,10 @@ struct mt_elf { GElf_Addr pltgot; }; -struct elf_image { - void *addr; /* pointer to mmap'd image */ - size_t size; /* (file-) size of the image */ -}; - int elf_read_library(struct task *task, struct libref *libref, const char *filename, GElf_Addr bias); /* Create a library object representing the main binary. */ -struct libref *elf_read_main_binary(struct task *task); +struct libref *elf_read_main_binary(struct task *task, int was_attached); #endif diff --git a/options.c b/options.c index b23b95c..97cde33 100644 --- a/options.c +++ b/options.c @@ -39,6 +39,7 @@ #include #include "common.h" +#include "debug.h" #include "options.h" #ifndef SYSCONFDIR @@ -171,11 +172,11 @@ static char *search_for_command(char *filename) static int add_opt_F(char *filename) { - struct opt_F_t *tmp = malloc(sizeof(*tmp)); - if (access(filename, R_OK)) return -1; + struct opt_F_t *tmp = malloc(sizeof(*tmp)); + if (!tmp) { fprintf(stderr, "%s\n", strerror(errno)); exit(1); diff --git a/report.c b/report.c index ea54a5f..2afcdc0 100644 --- a/report.c +++ b/report.c @@ -73,8 +73,6 @@ static void report_alloc64(struct task *task, enum mt_operation op, unsigned lon } } - skip_breakpoint(task, task->event.e_un.breakpoint); - server_send_msg(op, task->leader->pid, alloc, sizeof(*alloc) + i * sizeof(uint64_t)); } @@ -108,8 +106,6 @@ static void report_alloc32(struct task *task, enum mt_operation op, unsigned lon } } - skip_breakpoint(task, task->event.e_un.breakpoint); - server_send_msg(op, task->leader->pid, alloc, sizeof(*alloc) + i * sizeof(uint32_t)); } @@ -139,6 +135,23 @@ static void _report_malloc(struct task *task, struct library_symbol *libsym) _report_alloc_op(task, libsym, MT_MALLOC); } +#if 0 +static void _report_malloc1(struct task *task, struct library_symbol *libsym) +{ + unsigned long ret = fetch_retval(task); + + report_alloc(task, MT_MALLOC, ret, 1, options.bt_depth, libsym); +} +#endif + +static void _report_reallocarray(struct task *task, struct library_symbol *libsym) +{ + unsigned long size = fetch_param(task, 1) * fetch_param(task, 2); + unsigned long ret = fetch_retval(task); + + report_alloc(task, MT_MALLOC, ret, size, options.bt_depth, libsym); +} + static void _report_new(struct task *task, struct library_symbol *libsym) { _report_alloc_op(task, libsym, options.sanity ? MT_NEW : MT_MALLOC); @@ -173,31 +186,34 @@ static void report_delete_array(struct task *task, struct library_symbol *libsym static void _report_realloc(struct task *task, struct library_symbol *libsym) { + unsigned long addr = fetch_param(task, 0); + unsigned long size = fetch_param(task, 1); unsigned long ret = fetch_retval(task); - if (ret) { - unsigned long size = fetch_param(task, 1); - - report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym); + if (!addr) { + if (ret) + report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym); + return; } - if (fetch_param(task, 0)) { - if (task_is_64bit(task)) { - struct mt_alloc_payload_64 *alloc = alloca(sizeof(*alloc)); + if (ret) + report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym); - alloc->ptr = (uint64_t)ret; - alloc->size = (uint64_t)task->pid; + if (task_is_64bit(task)) { + struct mt_alloc_payload_64 *alloc = alloca(sizeof(*alloc)); - server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc)); - } - else { - struct mt_alloc_payload_32 *alloc = alloca(sizeof(*alloc)); + alloc->ptr = (uint64_t)ret; + alloc->size = (uint64_t)task->pid; - alloc->ptr = (uint32_t)ret; - alloc->size = (uint32_t)task->pid; + server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc)); + } + else { + struct mt_alloc_payload_32 *alloc = alloca(sizeof(*alloc)); - server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc)); - } + alloc->ptr = (uint32_t)ret; + alloc->size = (uint32_t)task->pid; + + server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc)); } } @@ -205,7 +221,8 @@ static void report_realloc(struct task *task, struct library_symbol *libsym) { unsigned long addr = fetch_param(task, 0); - report_alloc(task, MT_REALLOC_ENTER, addr, task->pid, options.sanity ? options.bt_depth : 0, libsym); + if (addr) + report_alloc(task, MT_REALLOC_ENTER, addr, task->pid, options.sanity ? options.bt_depth : 0, libsym); } static void _report_calloc(struct task *task, struct library_symbol *libsym) @@ -350,6 +367,16 @@ static const struct function flist[] = { { "pvalloc", "pvalloc", 1, NULL, _report_pvalloc }, { "mremap", "mremap", 0, report_mremap, _report_mremap }, { "cfree", "cfree", 1, report_free, NULL }, + { "reallocarray", "reallocarray", 0, NULL, _report_reallocarray }, +#if 0 + { "strdup", "strdup", 0, NULL, _report_malloc1 }, + { "strndup", "strndup", 0, NULL, _report_malloc1 }, + { "__strdup", "__strdup", 0, NULL, _report_malloc1 }, + { "__strndup", "__strndup", 0, NULL, _report_malloc1 }, + { "asprintf", "asprintf", 0, NULL, _report_malloc1 }, + { "vasprintf", "vasprintf", 0, NULL, _report_malloc1 }, + { "__asprintf", "__asprintf", 0, NULL, _report_malloc1 }, +#endif { "new(unsigned int)", "_Znwj", 1, NULL, _report_new }, { "new[](unsigned int)", "_Znaj", 1, NULL, _report_new_array }, @@ -361,14 +388,32 @@ static const struct function flist[] = { { "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 }, + { "new(unsigned int, std::align_val_t, std::nothrow_t const&)", "_ZnwjSt11align_val_tRKSt9nothrow_t", 1, NULL, _report_new }, + { "new[](unsigned int, std::align_val_t, std::nothrow_t const&)", "_ZnajSt11align_val_tRKSt9nothrow_t", 1, NULL, _report_new_array }, + { "new(unsigned int, std::align_val_t)", "_ZnwjSt11align_val_t", 1, NULL, _report_new }, + { "new[](unsigned int, std::align_val_t)", "_ZnajSt11align_val_t", 1, NULL, _report_new_array }, + { "new(unsigned long, std::align_val_t, std::nothrow_t const&)", "_ZnwmSt11align_val_tRKSt9nothrow_t", 1, NULL, _report_new }, + { "new[](unsigned long, std::align_val_t, std::nothrow_t const&)", "_ZnamSt11align_val_tRKSt9nothrow_t", 1, NULL, _report_new_array }, + { "new(unsigned long, std::align_val_t)", "_ZnwmSt11align_val_t", 1, NULL, _report_new }, + { "new[](unsigned long, std::align_val_t)", "_ZnamSt11align_val_t", 1, NULL, _report_new_array }, + { "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 }, + { "delete(void*, unsigned int)", "_ZdlPvj", 1, report_delete, NULL }, + { "delete[](void*, unsigned int)", "_ZdaPvj", 1, report_delete_array, NULL }, { "delete(void*, unsigned long)", "_ZdlPvm", 1, report_delete, NULL }, - { "delete[](void*, unsigned long)", "_ZdaPvj", 1, report_delete_array, NULL }, - { "delete(void*, unsigned long)", "_ZdlPvj", 1, report_delete, NULL }, { "delete[](void*, unsigned long)", "_ZdaPvm", 1, report_delete_array, NULL }, + + { "delete(void*, std::align_val_t)", "_ZdlPvSt11align_val_t", 1, report_delete, NULL }, + { "delete[](void*, std::align_val_t)", "_ZdaPvSt11align_val_t", 1, report_delete_array, NULL }, + { "delete(void*, std::align_val_t, std::nothrow_t const&)", "_ZdlPvSt11align_val_tRKSt9nothrow_t", 1, report_delete, NULL }, + { "delete[](void*, std::align_val_t, std::nothrow_t const&)", "_ZdaPvSt11align_val_tRKSt9nothrow_t", 1, report_delete_array, NULL }, + { "delete(void*, unsigned int, std::align_val_t)", "_ZdlPvjSt11align_val_t", 1, report_delete, NULL }, + { "delete[](void*, unsigned int, std::align_val_t)", "_ZdaPvjSt11align_val_t", 1, report_delete_array, NULL }, + { "delete(void*, unsigned long, std::align_val_t)", "_ZdlPvmSt11align_val_t", 1, report_delete, NULL }, + { "delete[](void*, unsigned long, std::align_val_t)", "_ZdaPvmSt11align_val_t", 1, report_delete_array, NULL }, }; const struct function *flist_matches_symbol(const char *sym_name) @@ -466,9 +511,9 @@ int report_scan(pid_t pid, const void *data, unsigned int data_len) return server_send_msg(MT_SCAN, pid, data, data_len); } -int report_attach(struct task *task) +int report_attach(struct task *task, int was_attached) { - struct mt_attached_payload state = { .attached = task->attached }; + struct mt_attached_payload state = { .attached = !!was_attached }; if (!server_connected()) return -1; @@ -530,7 +575,7 @@ static void report_process(struct task *leader) { struct list_head *it; - report_attach(leader); + report_attach(leader, 1); list_for_each(it, &leader->libraries_list) { struct library *lib = container_of(it, struct library, list); diff --git a/report.h b/report.h index 03dd6f5..f4ee764 100644 --- a/report.h +++ b/report.h @@ -23,6 +23,8 @@ #ifndef _INC_REPORT_H #define _INC_REPORT_H +#include + #include "forward.h" struct function { @@ -44,7 +46,7 @@ int report_add_map(struct task *task, struct library *lib); int report_del_map(struct task *task, struct library *lib); int report_info(int do_trace); int report_scan(pid_t pid, const void *data, unsigned int data_len); -int report_attach(struct task *task); +int report_attach(struct task *task, int was_attached); int report_fork(struct task *task, struct task *ptask); int report_exit(struct task *task); int report_about_exit(struct task *task); diff --git a/server.c b/server.c index 8e564e4..a1387f8 100644 --- a/server.c +++ b/server.c @@ -241,6 +241,8 @@ static void *server_listen_thread(void *ptr) { int ret; + (void)ptr; + for(;;) { if (!server_connected()) { thread_enable_cancel(); @@ -307,6 +309,8 @@ static void *server_pair_thread(void *ptr) { int ret; + (void)ptr; + for(;;) { if (!server_connected()) break; @@ -389,8 +393,11 @@ int server_stop(void) if (listen_fd != -1) { thread_cancel(thread); - if (thread) + if (thread) { thread_join(thread); + free(thread); + thread = NULL; + } if (is_named(options.address)) unlink(options.address); diff --git a/sysdeps/linux-gnu/os.c b/sysdeps/linux-gnu/os.c index c7d2e00..99d3184 100644 --- a/sysdeps/linux-gnu/os.c +++ b/sysdeps/linux-gnu/os.c @@ -63,6 +63,9 @@ struct map { static void report_fault(int signo, siginfo_t* siginf, void* arg) { + (void)siginf; + (void)arg; + fprintf(stderr, "fault signal %d (%s)\n", signo, strsignal(signo)); #ifndef DISABLE_CLIENT @@ -148,6 +151,8 @@ skip: static void signal_exit(int sig) { + (void)sig; + signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); @@ -366,28 +371,6 @@ void change_uid(void) } } -ssize_t sock_fd_write(int sock, void *buf, ssize_t buflen, int fd) -{ - ssize_t size; - struct msghdr msg; - struct iovec iov; - - iov.iov_base = buf; - iov.iov_len = buflen; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - msg.msg_control = NULL; - msg.msg_controllen = 0; - - size = sendmsg(sock, &msg, MSG_DONTWAIT); - - return size; -} - int os_init(void) { struct sigaction act; diff --git a/sysdeps/linux-gnu/proc.c b/sysdeps/linux-gnu/proc.c index 7b09bc7..10e9ea6 100644 --- a/sysdeps/linux-gnu/proc.c +++ b/sysdeps/linux-gnu/proc.c @@ -489,6 +489,8 @@ static int rdebug_bp_on_hit(struct task *task, struct breakpoint *bp) struct lt_r_debug_64 rdbg; struct task *leader = task->leader; + (void)bp; + debug(DEBUG_FUNCTION, "pid=%d", task->pid); if (load_debug_struct(task, leader->os.debug_addr, &rdbg) < 0) @@ -615,6 +617,7 @@ int os_task_init(struct task *task) void os_task_destroy(struct task *task) { + (void)task; } int os_task_clone(struct task *retp, struct task *task) diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c index b8a4b71..ce97ddf 100644 --- a/sysdeps/linux-gnu/trace.c +++ b/sysdeps/linux-gnu/trace.c @@ -52,34 +52,46 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static volatile pid_t wakeup_pid = -1; -static int inline task_kill(struct task *task, int sig) +static inline int task_kill(struct task *task, int sig) { errno = 0; return syscall(__NR_tgkill, task->leader->pid, task->pid, sig); } +static inline int wait_task(struct task *task, int *status) +{ + int ret; + + ret = TEMP_FAILURE_RETRY(waitpid(task ? task->pid : -1, status, __WALL)); + if (ret == -1) { + if (task) + fprintf(stderr, "!!!%s: waitpid pid=%d %s\n", __func__, task->pid, strerror(errno)); + } + return ret; +} + static int trace_setup(struct task *task, int status, int signum) { - int stop_signal; + int sig; - task->traced = 1; + task->attached = 1; task->stopped = 1; task->leader->threads_stopped++; - task->event.type = EVENT_SIGNAL; + task->event.type = EVENT_NEW; task->event.e_un.signum = 0; if (!WIFSTOPPED(status)) { - fprintf(stderr, "%s pid=%d not stopped\n", __FUNCTION__, task->pid); + fprintf(stderr, "!!!pid=%d not stopped\n", task->pid); return -1; } - stop_signal = WSTOPSIG(status); + sig = WSTOPSIG(status); - if (stop_signal != signum) { - task->event.e_un.signum = stop_signal; + if (sig != signum) { + task->event.e_un.signum = sig; - fprintf(stderr, "%s pid=%d unexpected trace signal (got:%d expected:%d)\n", __FUNCTION__, task->pid, stop_signal, signum); + fprintf(stderr, "!!!pid=%d unexpected trace signal (got:%d expected:%d)\n", task->pid, sig, signum); return -1; } @@ -90,10 +102,8 @@ static int _trace_wait(struct task *task, int signum) { int status; - if (unlikely(TEMP_FAILURE_RETRY(waitpid(task->pid, &status, __WALL)) != task->pid)) { - fprintf(stderr, "%s pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno)); + if (unlikely(wait_task(task, &status) == -1)) return -1; - } if (WIFEXITED(status)) return -1; @@ -103,11 +113,11 @@ static int _trace_wait(struct task *task, int signum) int trace_wait(struct task *task) { + assert(task->attached == 0); + if (_trace_wait(task, SIGTRAP)) return -1; - queue_event(task); - return 0; } @@ -115,6 +125,8 @@ static int child_event(struct task *task, enum event_type ev) { unsigned long data; + debug(DEBUG_EVENT, "child event %d pid=%d, newpid=%d", ev, task->pid, task->event.e_un.newpid); + if (unlikely(ptrace(PTRACE_GETEVENTMSG, task->pid, NULL, &data) == -1)) { debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno)); return -1; @@ -128,10 +140,7 @@ static int child_event(struct task *task, enum event_type ev) if (unlikely(!child)) return -1; - if (_trace_wait(child, SIGSTOP)) { - remove_task(child); - return -1; - } + child->attached = 1; } task->event.e_un.newpid = pid; @@ -142,44 +151,59 @@ static int child_event(struct task *task, enum event_type ev) static int _process_event(struct task *task, int status) { - int stop_signal; + int sig = WSTOPSIG(status); + + task->stopped = 1; + + assert(task->event.type == EVENT_NONE); if (WIFSIGNALED(status)) { + debug(DEBUG_EVENT, "EXIT_SIGNAL: pid=%d, signum=%d", task->pid, task->event.e_un.signum); + task->event.type = EVENT_EXIT_SIGNAL; task->event.e_un.signum = WTERMSIG(status); - debug(DEBUG_EVENT, "EXIT_SIGNAL: pid=%d, signum=%d", task->pid, task->event.e_un.signum); return 0; } if (WIFEXITED(status)) { + debug(DEBUG_EVENT, "EXIT: pid=%d, status=%d", task->pid, task->event.e_un.ret_val); + task->event.type = EVENT_EXIT; task->event.e_un.ret_val = WEXITSTATUS(status); - debug(DEBUG_EVENT, "EXIT: pid=%d, status=%d", task->pid, task->event.e_un.ret_val); return 0; } if (!WIFSTOPPED(status)) { - /* should never happen */ - debug(DEBUG_EVENT, "NONE: pid=%d ???", task->pid); + fprintf(stderr, "!!!not WIFSTOPPED pid=%d\n", task->pid); return -1; } + if (unlikely(task->is_new)) { + if (sig == SIGSTOP && !(status >> 16)) { + task->event.type = EVENT_NEW; + task->event.e_un.signum = 0; + + return 0; + } + + if (!task->bad) { + fprintf(stderr, "!!!unexpected state for pid=%d, expected signal SIGSTOP (%d %d)\n", task->pid, sig, status >> 16); + task->bad = 1; + } + } + switch(status >> 16) { + case 0: + break; case PTRACE_EVENT_VFORK: - if (child_event(task, EVENT_VFORK)) - return -1; debug(DEBUG_EVENT, "VFORK: pid=%d, newpid=%d", task->pid, task->event.e_un.newpid); - return 0; + return child_event(task, EVENT_VFORK); case PTRACE_EVENT_FORK: - if (child_event(task, EVENT_FORK)) - return -1; debug(DEBUG_EVENT, "FORK: pid=%d, newpid=%d", task->pid, task->event.e_un.newpid); - return 0; + return child_event(task, EVENT_FORK); case PTRACE_EVENT_CLONE: - if (child_event(task, EVENT_CLONE)) - return -1; debug(DEBUG_EVENT, "CLONE: pid=%d, newpid=%d", task->pid, task->event.e_un.newpid); - return 0; + return child_event(task, EVENT_CLONE); case PTRACE_EVENT_EXEC: task->event.type = EVENT_EXEC; debug(DEBUG_EVENT, "EXEC: pid=%d", task->pid); @@ -188,64 +212,77 @@ static int _process_event(struct task *task, int status) { unsigned long data; + debug(DEBUG_EVENT, "ABOUT_EXIT: pid=%d", task->pid); + if (unlikely(ptrace(PTRACE_GETEVENTMSG, task->pid, NULL, &data) == -1)) { debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno)); return -1; } task->event.e_un.ret_val = WEXITSTATUS(data); task->event.type = EVENT_ABOUT_EXIT; - debug(DEBUG_EVENT, "ABOUT_EXIT: pid=%d", task->pid); return 0; } default: + fprintf(stderr, "!!!PTRACE_EVENT_????? pid=%d %d\n", task->pid, status >> 16); break; } - stop_signal = WSTOPSIG(status); + if (!sig) + fprintf(stderr, "!!!%s: sig == 0 pid=%d\n", __func__, task->pid); + + if (sig == SIGSTOP) { + siginfo_t siginfo; + + if (unlikely(ptrace(PTRACE_GETSIGINFO, task->pid, 0, &siginfo) == -1)) + sig = 0; + else { + if (likely(siginfo.si_pid == mtrace_pid)) + sig = 0; + else + fprintf(stderr, "!!!%s: SIGSTOP pid=%d %d %d %d %d\n", __func__, task->pid, siginfo.si_signo, siginfo.si_errno, siginfo.si_code, siginfo.si_pid); + } + } task->event.type = EVENT_SIGNAL; - task->event.e_un.signum = stop_signal; + task->event.e_un.signum = sig; - debug(DEBUG_EVENT, "SIGNAL: pid=%d, signum=%d", task->pid, stop_signal); - return stop_signal; + debug(DEBUG_EVENT, "SIGNAL: pid=%d, signum=%d", task->pid, sig); + return sig; } -static void process_event(struct task *task, int status) +static struct task * process_event(struct task *task, int status) { - int stop_signal; struct task *leader = task->leader; struct breakpoint *bp = NULL; arch_addr_t ip; + int sig; + assert(task->stopped == 0); assert(leader != NULL); if (unlikely(options.verbose > 1)) start_time(&task->halt_time); - task->stopped = 1; - leader->threads_stopped++; - stop_signal = _process_event(task, status); - - if (stop_signal == -1) { -fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); - task->event.type = EVENT_NONE; + sig = _process_event(task, status); + if (sig < 0) { continue_task(task, 0); - return; + return NULL; } - if (stop_signal == 0) - return; + if (task->event.type == EVENT_NONE) { + continue_task(task, task->event.e_un.signum); + return NULL; + } - if (unlikely(stop_signal != SIGTRAP)) - return; + if (unlikely(sig != SIGTRAP)) + return task; if (unlikely(fetch_context(task) == -1)) { -fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); task->event.type = EVENT_NONE; continue_task(task, 0); - return; + return NULL; } ip = get_instruction_pointer(task); @@ -270,10 +307,8 @@ fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); { bp = breakpoint_find(leader, ip - DECR_PC_AFTER_BREAK); if (unlikely(!bp)) { -fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); -// task->event.type = EVENT_NONE; -// continue_task(task, 0); - return; + fprintf(stderr, "!!!%s: SIGTRAP pid=%d\n", __func__, task->pid); + return task; } #if HW_BREAKPOINTS > 0 assert(bp->type != BP_HW_SCRATCH); @@ -293,23 +328,7 @@ fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); debug(DEBUG_EVENT, "BREAKPOINT: pid=%d, addr=%#lx", task->pid, task->event.e_un.breakpoint->addr); - return; -} - -static void trace_fail_warning(void) -{ -#ifdef HAVE_LIBSELINUX - if (security_get_boolean_active("deny_ptrace") == 1) - fprintf(stderr, - "The SELinux boolean 'deny_ptrace' is enabled, which may prevent mtrace-ng\n" - "from tracing an other task. You can disable this process attach protection by\n" - "issuing 'setsebool deny_ptrace=0' in the superuser context.\n"); -#else - fprintf(stderr, - "Could not trace! Maybe the SELinux boolean 'deny_ptrace' is enabled, which may\n" - "prevent mtrace-ng from tracing an other tasks. Try to disable this process attach\n" - "protection by issuing 'setsebool deny_ptrace=0' in the superuser context.\n"); -#endif + return task; } void trace_me(void) @@ -324,25 +343,25 @@ void trace_me(void) } } -static inline int fix_signal(struct task *task, int signum) +static inline int chk_signal(struct task *task, int signum) { - if (signum == SIGSTOP && task->was_stopped) { - task->was_stopped = 0; - signum = 0; - } +#if 1 + if (signum == SIGSTOP) + fprintf(stderr, "!!!%s: SIGSTOP pid=%d\n", __func__, task->pid); + + if (signum == SIGTRAP) + fprintf(stderr, "!!!%s: SIGTRAP pid=%d\n", __func__, task->pid); +#endif + return signum; } -int untrace_task(struct task *task, int signum) +int untrace_task(struct task *task) { - int ret = 0; + int ret; + int sig = 0; - assert(task->leader != NULL); - - debug(DEBUG_PROCESS, "pid=%d", task->pid); - - if (!task->stopped) - return 0; + assert(task->stopped); if (unlikely(ptrace(PTRACE_SETOPTIONS, task->pid, 0, (void *)0) == -1)) { if (errno != ESRCH) @@ -351,45 +370,53 @@ int untrace_task(struct task *task, int signum) goto skip; } - signum = fix_signal(task, signum); + if (task->event.type == EVENT_SIGNAL || task->event.type == EVENT_NONE) + sig = chk_signal(task, sig); - if (unlikely(ptrace(PTRACE_DETACH, task->pid, 0, signum) == -1)) { - if (task->traced) { - if (errno != ESRCH) - fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno)); - ret = -1; - goto skip; - } + if (unlikely(ptrace(PTRACE_DETACH, task->pid, 0, sig) == -1)) { + if (errno != ESRCH) + fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno)); + ret = -1; } task_kill(task, SIGCONT); skip: task->leader->threads_stopped--; task->stopped = 0; - task->was_stopped = 0; - task->traced = 0; + task->attached = 0; return ret; } +void stop_task(struct task *task) +{ + assert(task->attached); + assert(task->leader != NULL); + + if (!task->stopped) { + int status; + + task_kill(task, SIGSTOP); + if (wait_task(task, &status) != -1) + _process_event(task, status); + } +} + int trace_attach(struct task *task) { debug(DEBUG_PROCESS, "pid=%d", task->pid); - assert(task->traced == 0); + assert(task->attached == 0); if (unlikely(ptrace(PTRACE_ATTACH, task->pid, 0, 0) == -1)) { if (errno != ESRCH) fprintf(stderr, "PTRACE_ATTACH pid=%d %s\n", task->pid, strerror(errno)); - trace_fail_warning(); return -1; } if (_trace_wait(task, SIGSTOP)) return -1; - queue_event(task); - return 0; } @@ -409,15 +436,22 @@ int trace_set_options(struct task *task) int continue_task(struct task *task, int signum) { - debug(DEBUG_PROCESS, "pid=%d", task->pid); + debug(DEBUG_PROCESS, "continue task pid=%d", task->pid); assert(task->leader != NULL); assert(task->stopped); + if (signum >= 0x80) + fprintf(stderr, "!!!signum >= 0x80 pid=%d: %d\n", task->pid, signum); + task->leader->threads_stopped--; task->stopped = 0; + task->event.type = EVENT_NONE; - if (unlikely(ptrace(PTRACE_CONT, task->pid, 0, fix_signal(task, signum)) == -1)) { + if (signum == SIGTRAP) + fprintf(stderr, "!!!%s: SIGTRAP pid=%d\n", __func__, task->pid); + + if (unlikely(ptrace(PTRACE_CONT, task->pid, 0, chk_signal(task, signum)) == -1)) { if (errno != ESRCH) fprintf(stderr, "PTRACE_CONT pid=%d %s\n", task->pid, strerror(errno)); return -1; @@ -427,10 +461,12 @@ int continue_task(struct task *task, int signum) static void do_stop_cb(struct task *task, void *data) { + (void)data; + if (task->stopped) return; - task->was_stopped = 1; + debug(DEBUG_EVENT, "task stop pid=%d", task->pid); task_kill(task, SIGSTOP); } @@ -441,6 +477,8 @@ void stop_threads(struct task *task) assert(task->leader != NULL); + debug(DEBUG_EVENT, "stop threads pid=%d", task->pid); + if (leader->threads != leader->threads_stopped) { struct timespec start; @@ -449,8 +487,12 @@ void stop_threads(struct task *task) each_task(leader, &do_stop_cb, NULL); - while (leader->threads != leader->threads_stopped) - queue_event(wait_event()); + while (leader->threads != leader->threads_stopped) { + task = wait_event(); + + if (task) + queue_event(task); + } if (unlikely(options.verbose > 1)) set_timer(&start, &stop_time); @@ -460,52 +502,53 @@ void stop_threads(struct task *task) int handle_singlestep(struct task *task, int (*singlestep)(struct task *task), struct breakpoint *bp) { int status; - int stop_signal; - unsigned long ip; + int sig; - for(;;) { - if (unlikely(singlestep(task) == -1)) - return -1; + assert(task->stopped); + assert(task->skip_bp == NULL); + assert(bp->enabled == 0); - if (unlikely(TEMP_FAILURE_RETRY(waitpid(task->pid, &status, __WALL)) != task->pid)) { - fprintf(stderr, "%s waitpid pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno)); - return 0; - } + task->event.type = EVENT_NONE; - stop_signal = _process_event(task, status); - - if (stop_signal == -1) - return 0; - - ip = ptrace(PTRACE_PEEKUSER, task->pid, ip_reg_addr(), 0); - if (ip == (unsigned long)-1) { - fprintf(stderr, "%s ptrace get IP pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno)); - return 0; - } - - if (ip != bp->addr) { - if (likely(stop_signal == SIGTRAP)) { - if (bp->break_insn) { - queue_event(task); - return 1; - } - return 0; - } - } - - if (likely(!stop_signal)) { - queue_event(task); - return 1; - } - - if (fix_signal(task, stop_signal) > 0) { - queue_event(task); - return 1; - } - - if (ip != bp->addr) - return 0; + if (unlikely(singlestep(task) == -1)) { + fprintf(stderr, "!!!%s: single step failed pid=%d\n", __func__, task->pid); + return -1; } + + if (unlikely(wait_task(task, &status) == -1)) + return 0; + + sig = _process_event(task, status); + + if (sig == -1) { + fprintf(stderr, "!!!%s: failed _process_event pid=%d\n", __func__, task->pid); + return 0; + } + + assert(task->stopped); + assert(task->event.type != EVENT_NONE); + assert(task->event.type != EVENT_BREAKPOINT); + + if (task->event.type != EVENT_SIGNAL) { + queue_event(task); + return 1; + } + + if (sig != SIGTRAP) { + if (sig == SIGSTOP) + fprintf(stderr, "!!!%s: SIGSTOP pid=%d\n", __func__, task->pid); + queue_event(task); + return 1; + } + + if (bp->break_insn) { + queue_event(task); + return 0; + } + + task->event.type = EVENT_BREAKPOINT; + task->event.e_un.breakpoint = bp; + return 0; } #ifndef ARCH_SINGLESTEP @@ -513,7 +556,7 @@ static int ptrace_singlestep(struct task *task) { if (unlikely(ptrace(PTRACE_SINGLESTEP, task->pid, 0, 0) == -1)) { if (errno != ESRCH) - fprintf(stderr, "%s PTRACE_SINGLESTEP pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno)); + fprintf(stderr, "!!!%s: PTRACE_SINGLESTEP pid=%d %s\n", __func__, task->pid, strerror(errno)); return -1; } return 0; @@ -531,14 +574,10 @@ struct task *wait_event(void) int status; int pid; - pid = waitpid(-1, &status, __WALL); + pid = wait_task(NULL, &status); if (unlikely(pid == -1)) { - if (errno != EINTR) { - if (errno == ECHILD) - debug(DEBUG_EVENT, "No more traced programs"); - else - fprintf(stderr, "%s waitpid %s\n", __FUNCTION__, strerror(errno)); - } + if (errno == ECHILD) + debug(DEBUG_EVENT, "No more traced programs"); return NULL; } @@ -558,12 +597,14 @@ struct task *wait_event(void) if (likely(task)) trace_setup(task, status, SIGSTOP); - return NULL; } - process_event(task, status); + assert(!task->stopped); + task = process_event(task, status); + if (task) + assert(task->stopped); return task; } @@ -618,7 +659,7 @@ ssize_t copy_from_proc(struct task *task, arch_addr_t addr, void *dst, size_t le if (errno != EFAULT) { if (errno != ENOSYS) { - fprintf(stderr, "%s pid=%d process_vm_readv: %s\n", __FUNCTION__, task->pid, strerror(errno)); + fprintf(stderr, "!!!%s: pid=%d process_vm_readv: %s\n", __func__, task->pid, strerror(errno)); return -1; } @@ -755,10 +796,10 @@ ssize_t copy_str_from_proc(struct task *task, arch_addr_t addr, char *dst, size_ errno = 0; - if (--len < 0) + if (!len--) return -1; - while (len) { + while(len) { a.a = ptrace(PTRACE_PEEKTEXT, task->pid, addr, 0); if (unlikely(a.a == -1 && errno)) { if (num_bytes && errno == EIO) diff --git a/sysdeps/linux-gnu/x86/arch.c b/sysdeps/linux-gnu/x86/arch.c index fcc8d85..b3dbc5d 100644 --- a/sysdeps/linux-gnu/x86/arch.c +++ b/sysdeps/linux-gnu/x86/arch.c @@ -106,17 +106,17 @@ static int set_breakpoint_mode(struct task *task, unsigned int n, int type, int uint32_t mode; uint32_t dr7, mask; - mask = (0b1111 << (16 + 4 * n)) | (0b11 << (2 * n)); + mask = (0b1111U << (16 + 4 * n)) | (0b11U << (2 * n)); switch(type) { case BP_X: - mode = 0b0000; + mode = 0b0000U; break; case BP_W: - mode = 0b0001; + mode = 0b0001U; break; case BP_RW: - mode = 0b0011; + mode = 0b0011U; break; default: fprintf(stderr, "invalid hw breakpoint type\n"); @@ -125,16 +125,16 @@ static int set_breakpoint_mode(struct task *task, unsigned int n, int type, int switch(len) { case 1: - mode |= 0b0000; + mode |= 0b0000U; break; case 2: - mode |= 0b0100; + mode |= 0b0100U; break; case 4: - mode |= 0b1100; + mode |= 0b1100U; break; case 8: - mode |= 0b1000; + mode |= 0b1000U; break; } @@ -143,19 +143,19 @@ static int set_breakpoint_mode(struct task *task, unsigned int n, int type, int dr7 |= mode << (16 + 4 * n); if (local) { - dr7 |= 0b01 << (2 * n); + dr7 |= 0b01U << (2 * n); dr7 |= 1 << 8; } else - if (!(dr7 & 0b01010101)) + if (!(dr7 & 0b01010101U)) dr7 &= ~(1 << 8); if (global) { - dr7 |= 0b10 << (2 * n); + dr7 |= 0b10U << (2 * n); dr7 |= 1 << 9; } else - if (!(dr7 & 0b10101010)) + if (!(dr7 & 0b10101010U)) dr7 &= ~(1 << 9); return apply_hw_bp(task, dr7); @@ -183,14 +183,14 @@ int reset_hw_bp(struct task *task, unsigned int n) { uint32_t dr7, mask; - mask = (0b1111 << (16 + 4 * n)) | (0b11 << (2 * n)); + mask = (0b1111U << (16 + 4 * n)) | (0b11U << (2 * n)); dr7 = task->arch.dr7 & ~mask; - if (!(dr7 & 0b01010101)) + if (!(dr7 & 0b01010101U)) dr7 &= ~(1 << 8); - if (!(dr7 & 0b10101010)) + if (!(dr7 & 0b10101010U)) dr7 &= ~(1 << 9); return apply_hw_bp(task, dr7); @@ -210,7 +210,7 @@ int arch_task_init(struct task *task) { unsigned int i; - for(i = 0; i < HW_BREAKPOINTS; ++i) + for(i = 0; i != HW_BREAKPOINTS; ++i) task->arch.hw_bp[i] = 0; return _apply_hw_bp(task, 0); @@ -223,6 +223,9 @@ void arch_task_destroy(struct task *task) int arch_task_clone(struct task *retp, struct task *task) { + (void)retp; + (void)task; + return 0; } diff --git a/sysdeps/linux-gnu/x86/dwarf-x86.c b/sysdeps/linux-gnu/x86/dwarf-x86.c index ff645e8..8b50833 100644 --- a/sysdeps/linux-gnu/x86/dwarf-x86.c +++ b/sysdeps/linux-gnu/x86/dwarf-x86.c @@ -137,6 +137,7 @@ static inline int is_signal_frame(struct dwarf_cursor *c) static inline int is_plt_entry(struct dwarf_addr_space *as) { + (void)as; #if 0 struct dwarf_cursor *c = &as->cursor; uint8_t data[12]; @@ -334,7 +335,7 @@ int dwarf_arch_check_call(struct dwarf_addr_space *as, arch_addr_t ip) unsigned int i; unsigned char *addr = libref->image_addr + ip - p->off - libref->load_addr; - for(i = 0; i < call_op[i].len; ++i) { + for(i = 0; i < p->len; ++i) { if (unlikely((addr[i] & p->mask[i]) != p->op[i])) break; } diff --git a/task.c b/task.c index 642bda6..926d332 100644 --- a/task.c +++ b/task.c @@ -54,15 +54,19 @@ struct pid_hash *pid_hash[PID_HASH_SIZE]; #ifndef OS_HAVE_PROCESS_DATA static inline int os_task_init(struct task *task) { + (void)task; return 0; } static inline void os_task_destroy(struct task *task) { + (void)task; } -static inline int os_task_clone(struct task *retp, struct task *task) +static inline int os_task_clone(struct task *task, struct task *newtask) { + (void)task; + (void)newtask; return 0; } #endif @@ -70,15 +74,19 @@ static inline int os_task_clone(struct task *retp, struct task *task) #ifndef ARCH_HAVE_PROCESS_DATA static inline int arch_task_init(struct task *task) { + (void)task; return 0; } static inline void arch_task_destroy(struct task *task) { + (void)task; } -static inline int arch_task_clone(struct task *retp, struct task *task) +static inline int arch_task_clone(struct task *task, struct task *newtask) { + (void)task; + (void)newtask; return 0; } #endif @@ -101,29 +109,53 @@ static inline void delete_pid(struct task *task) static inline void insert_pid(struct task *task) { - struct pid_hash *entry = pid_hash[PID_HASH(task->pid)]; + unsigned int pidhash = PID_HASH(task->pid); + struct pid_hash *entry = pid_hash[pidhash]; - if (!entry->size) { + if (!entry) { entry = malloc(sizeof(*entry) + 8 * sizeof(entry->tasks[0])); entry->num = 0; entry->size = 8; - pid_hash[PID_HASH(task->pid)] = entry; + pid_hash[pidhash] = entry; } else if (entry->size == entry->num) { entry->size += 8; entry = realloc(entry, sizeof(*entry) + entry->size * sizeof(entry->tasks[0])); - pid_hash[PID_HASH(task->pid)] = entry; + pid_hash[pidhash] = entry; } entry->tasks[entry->num++] = task; } -static int leader_setup(struct task *leader) +struct task *pid2task(pid_t pid) { - if (!elf_read_main_binary(leader)) + struct pid_hash *entry = pid_hash[PID_HASH(pid)]; + + if (!entry) + return NULL; + + struct task **p = entry->tasks; + unsigned int n = entry->num; + + while(n) { + struct task *task = *p; + + if (likely(task->pid == pid)) + return task; + + p++; + n--; + } + + return NULL; +} + +static int leader_setup(struct task *leader, int was_attached) +{ + if (!elf_read_main_binary(leader, was_attached)) return -1; return backtrace_init(leader); @@ -175,8 +207,6 @@ static int task_init(struct task *task) list_add_tail(&task->task_list, &leader->task_list); } - task->attached = 1; - breakpoint_hw_destroy(task); return 0; @@ -216,10 +246,18 @@ static void task_destroy(struct task *task) task->deleted = 1; + stop_task(task); + + if (task->event.type == EVENT_BREAKPOINT) + breakpoint_put(task->event.e_un.breakpoint); + arch_task_destroy(task); os_task_destroy(task); - detach_task(task); + task_reset_bp(task); + remove_event(task); + breakpoint_hw_destroy(task); delete_pid(task); + untrace_task(task); if (leader != task) { list_del(&task->task_list); @@ -239,9 +277,11 @@ struct task *task_new(pid_t pid) memset(task, 0, sizeof(*task)); task->pid = pid; - task->traced = 0; + task->attached = 0; task->stopped = 0; - task->was_stopped = 0; + task->is_new = 1; + task->defer_func = NULL; + task->defer_data = NULL; INIT_LIST_HEAD(&task->task_list); INIT_LIST_HEAD(&task->leader_list); @@ -278,8 +318,9 @@ int process_exec(struct task *task) { struct task *leader = task->leader; + breakpoint_invalidate_all(leader); + each_task(leader, &remove_task_cb, leader); - breakpoint_disable_all(leader); os_task_destroy(leader); arch_task_destroy(leader); @@ -290,13 +331,10 @@ int process_exec(struct task *task) if (task_init(leader) < 0) goto fail; - if (server_connected()) - task->attached = 0; - assert(leader->leader == leader); assert(leader->threads_stopped == 1); - if (leader_setup(leader) < 0) + if (leader_setup(leader, 0) < 0) goto fail; return 0; @@ -310,8 +348,6 @@ struct task *task_create(char **argv) struct task *task; pid_t pid; - debug(DEBUG_FUNCTION, "`%s'", options.command); - pid = fork(); if (pid < 0) { perror("fork"); @@ -320,6 +356,7 @@ struct task *task_create(char **argv) if (!pid) { /* child */ change_uid(); + trace_me(); execvp(options.command, argv); fprintf(stderr, "Can't execute `%s': %s\n", options.command, strerror(errno)); @@ -336,10 +373,10 @@ struct task *task_create(char **argv) if (trace_set_options(task) < 0) goto fail2; - if (server_connected()) - task->attached = 0; + if (leader_setup(task, 0) < 0) + goto fail1; - if (leader_setup(task) < 0) + if (handle_event(task)) goto fail1; return task; @@ -354,15 +391,11 @@ fail1: int task_clone(struct task *task, struct task *newtask) { + assert(newtask->attached); assert(newtask->leader != newtask); - assert(newtask->event.type == EVENT_SIGNAL); - assert(newtask->event.e_un.signum == 0); - assert(newtask->traced); - assert(newtask->stopped); assert(newtask->backtrace == NULL); newtask->is_64bit = task->is_64bit; - newtask->attached = task->attached; breakpoint_hw_clone(newtask); @@ -374,10 +407,7 @@ int task_fork(struct task *task, struct task *newtask) struct task *leader = task->leader; assert(newtask->leader == newtask); - assert(newtask->event.type == EVENT_SIGNAL); - assert(newtask->event.e_un.signum == 0); - assert(newtask->traced); - assert(newtask->stopped); + assert(newtask->attached); assert(newtask->backtrace == NULL); newtask->is_64bit = task->is_64bit; @@ -454,7 +484,9 @@ fail1: static void show_attached(struct task *task, void *data) { - fprintf(stderr, "+++ process pid=%d attached (%s) +++\n", task->pid, library_execname(task->leader)); + (void)data; + + fprintf(stderr, "+++ process pid=%d attached (%s)\n", task->pid, library_execname(task->leader)); } @@ -513,7 +545,7 @@ void open_pid(pid_t pid) old_ntasks = ntasks; } - if (leader_setup(leader) < 0) + if (leader_setup(leader, 1) < 0) goto fail1; list_for_each(it, &leader->task_list) { @@ -550,13 +582,12 @@ void each_task(struct task *leader, void (*cb)(struct task *task, void *data), v { struct list_head *it, *next; - (*cb)(leader, data); - list_for_each_safe(it, next, &leader->task_list) { struct task *task = container_of(it, struct task, task_list); (*cb)(task, data); }; + (*cb)(leader, data); } void remove_task(struct task *task) @@ -572,8 +603,18 @@ void remove_proc(struct task *leader) assert(leader->leader == leader); - breakpoint_disable_all(leader); - each_task(leader, &remove_task_cb, NULL); + each_task(leader, &remove_task_cb, leader); + task_destroy(leader); +} + +void untrace_proc(struct task *leader) +{ + debug(DEBUG_FUNCTION, "pid=%d", leader->pid); + + assert(leader->leader == leader); + + breakpoint_invalidate_all(leader); + remove_proc(leader); } int task_list_empty(void) @@ -587,26 +628,20 @@ void each_pid(void (*cb)(struct task *task)) for(i = 0; i < ARRAY_SIZE(pid_hash); ++i) { struct pid_hash *entry = pid_hash[i]; - unsigned int n = entry->num; - if (n) { - struct task **p = alloca(n * sizeof(*p)); + if (entry) { + unsigned int n = entry->num; - memcpy(p, entry->tasks, n * sizeof(*p)); + if (n) { + struct task **p = alloca(n * sizeof(*p)); - do { - (*cb)(*p++); - } while(--n); + memcpy(p, entry->tasks, n * sizeof(*p)); + + do { + (*cb)(*p++); + } while(--n); + } } } } -void init_pid_hash(void) -{ - static const struct pid_hash preset = { .size = 0, .num = 0 }; - unsigned int i; - - for(i = 0; i < ARRAY_SIZE(pid_hash); ++i) - pid_hash[i] = (void *)&preset; -} - diff --git a/task.h b/task.h index cf8ece4..e29e513 100644 --- a/task.h +++ b/task.h @@ -43,12 +43,12 @@ struct task { struct event event; unsigned int is_64bit:1; - unsigned int traced:1; unsigned int attached:1; unsigned int deleted:1; unsigned int about_exit:1; - unsigned int was_stopped:1; unsigned int stopped:1; + unsigned int is_new:1; + unsigned int bad:1; struct breakpoint *breakpoint; struct library_symbol *libsym; @@ -101,6 +101,12 @@ struct task { /* halt time for debugging purpose */ struct timespec halt_time; + /* defered event function */ + int (*defer_func)(struct task *task, void *data); + + /* defered event data */ + void *defer_data; + #if HW_BREAKPOINTS > 1 /* set in leader: list of hw breakpoints */ struct list_head hw_bp_list; @@ -144,9 +150,15 @@ void each_pid(void (*cb)(struct task *task)); /* Remove task from the list of traced processes, drop any events in the event queue, destroy it and free memory. */ void remove_task(struct task *task); +/* invalidate all breakpoints and call remove_proc */ +void untrace_proc(struct task *leader); + /* Remove all threads of the process from the list of traced processes, drop any events in the event queue, destroy it and free memory. */ void remove_proc(struct task *leader); +/* halt a task */ +void stop_task(struct task *task); + /* return true if no more task is traced */ int task_list_empty(void); @@ -166,27 +178,6 @@ struct pid_hash { #define PID_HASH_SIZE 256 extern struct pid_hash *pid_hash[PID_HASH_SIZE]; - -void init_pid_hash(void); - -static inline struct task *pid2task(pid_t pid) -{ - struct pid_hash *entry = pid_hash[PID_HASH(pid)]; - struct task **p = entry->tasks; - unsigned int n = entry->num; - - while(n) { - struct task *task = *p; - - if (likely(task->pid == pid)) - return task; - - p++; - n--; - } - - return NULL; -} - +extern struct task *pid2task(pid_t pid); #endif diff --git a/trace.c b/trace.c index e244cae..4db6a70 100644 --- a/trace.c +++ b/trace.c @@ -46,15 +46,18 @@ int skip_breakpoint(struct task *task, struct breakpoint *bp) { debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr); - if (task->event.type != EVENT_NONE) - return 1; + assert(task->event.type == EVENT_BREAKPOINT); + assert(task->stopped); + assert(task->skip_bp == NULL); if (bp->enabled && !bp->hw) { int ret = 0; struct timespec start; - if (task->skip_bp) + if (task->skip_bp) { + task->event.type = EVENT_NONE; return 1; + } if (unlikely(options.verbose > 1)) start_time(&start); @@ -68,6 +71,7 @@ int skip_breakpoint(struct task *task, struct breakpoint *bp) if (unlikely(ret)) { task->skip_bp = breakpoint_get(bp); + assert(task->skip_bp); return ret; } } @@ -85,26 +89,10 @@ void fix_about_exit(struct task *task) } } -void detach_task(struct task *task) -{ - int sig = 0; - - task_reset_bp(task); - - if (task->event.type == EVENT_SIGNAL) - sig = task->event.e_un.signum; - else - if (task->event.type == EVENT_BREAKPOINT) - breakpoint_put(task->event.e_un.breakpoint); - - remove_event(task); - breakpoint_hw_destroy(task); - fix_about_exit(task); - untrace_task(task, sig); -} - static void detach_cb(struct task *task, void *data) { + (void)data; + remove_task(task); } @@ -115,7 +103,7 @@ void detach_proc(struct task *leader) breakpoint_disable_all(leader); if (unlikely(options.verbose > 1)) - fprintf(stderr, "+++ process detach pid=%d +++\n", leader->pid); + fprintf(stderr, "+++ process detach pid=%d\n", leader->pid); each_task(leader, &detach_cb, NULL); } diff --git a/trace.h b/trace.h index 3d18ea2..168855c 100644 --- a/trace.h +++ b/trace.h @@ -26,7 +26,6 @@ #include "forward.h" void fix_about_exit(struct task *task); -void detach_task(struct task *task); void detach_proc(struct task *leader); int skip_breakpoint(struct task *task, struct breakpoint *bp);