bump to version 0.8

tons of fixes. On the way to a final release 1.0
This commit is contained in:
Stefani Seibold 2018-04-06 22:10:20 +02:00
parent 4c5f0c20b4
commit 0155d789b4
34 changed files with 872 additions and 559 deletions

View File

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.9)
set(MT "mtrace-ng") 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) 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) 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(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose type of build" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Relase" "LTO") "Debug" "Release" "LTO")
endif() endif()
include(CheckFunctionExists) include(CheckFunctionExists)
@ -45,10 +45,13 @@ include_directories(
"${PROJECT_SOURCE_DIR}/sysdeps" "${PROJECT_SOURCE_DIR}/sysdeps"
) )
set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -Wall") 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_C_FLAGS_RELEASE "-O3 -Wall -DNDEBUG") 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_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) check_ipo_supported(RESULT IPO)
#if (IPO) #if (IPO)

View File

@ -56,8 +56,8 @@ int trace_set_options(struct task *task);
/* make the forked process traceable */ /* make the forked process traceable */
void trace_me(void); void trace_me(void);
/* stop tracing a task. */ /* stop tracing of a task. */
int untrace_task(struct task *task, int signum); int untrace_task(struct task *task);
/* Called when mtrace-ng needs to attach to task */ /* Called when mtrace-ng needs to attach to task */
int trace_attach(struct task *task); int trace_attach(struct task *task);

View File

@ -70,28 +70,37 @@ static void enable_sw_breakpoint(struct task *task, struct breakpoint *bp)
{ {
static unsigned char break_insn[] = BREAKPOINT_VALUE; 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); 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); 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->break_insn = !memcmp(break_insn, bp->orig_value, BREAKPOINT_LENGTH);
bp->was_hw = 0; 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); 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); 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) 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_SW);
assert(bp->type < BP_HW || task->hw_bp[slot] == NULL); assert(bp->type < BP_HW || task->hw_bp[slot] == NULL);
assert(bp->sw == 0);
task->hw_bp[slot] = bp; 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; unsigned int slot = bp->hw_bp_slot;
assert(bp->sw == 0);
if (unlikely(!task->hw_bp[slot])) if (unlikely(!task->hw_bp[slot]))
return; 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; return;
}
qsort(bp_list, n, sizeof(*bp_list), hw_bp_sort); qsort(bp_list, n, sizeof(*bp_list), hw_bp_sort);
for(i = 0; i < n; ++i) 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) if (n > HW_BREAKPOINTS - 1)
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); assert((hw_bp_set & 1 << bp_list[i]->hw_bp_slot) == 0);
hw_bp_set |= 1 << bp_list[i]->hw_bp_slot; hw_bp_set |= 1 << bp_list[i]->hw_bp_slot;
continue;
} }
else
*p++ = bp_list[i]; *p++ = bp_list[i];
} }
if (p == bp_list) if (p == bp_list)
@ -231,6 +248,7 @@ void reorder_hw_bp(struct task *task)
if (leader->hw_bp[i]) if (leader->hw_bp[i])
hw2sw_bp(leader, leader->hw_bp[i]); hw2sw_bp(leader, leader->hw_bp[i]);
bp->enabled = 1;
bp->hw_bp_slot = i; bp->hw_bp_slot = i;
bp->hw = 1; bp->hw = 1;
bp->was_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->ext = ext;
bp->refcnt = 1; bp->refcnt = 1;
bp->count = 0; bp->count = 0;
bp->sw = 0;
#if HW_BREAKPOINTS > 1 #if HW_BREAKPOINTS > 1
bp->hwcnt = 0; bp->hwcnt = 0;
#endif #endif
@ -432,7 +451,6 @@ void breakpoint_enable(struct task *task, struct breakpoint *bp)
#endif #endif
bp->hw = 0; bp->hw = 0;
enable_sw_breakpoint(task, bp); enable_sw_breakpoint(task, bp);
bp->enabled = 1;
} }
} }
@ -467,7 +485,6 @@ void breakpoint_disable(struct task *task, struct breakpoint *bp)
} }
#endif #endif
disable_sw_breakpoint(task, bp); 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) static int enable_nonlocked_bp_cb(unsigned long key, const void *value, void *data)
{ {
(void)key;
struct breakpoint *bp = (struct breakpoint *)value; struct breakpoint *bp = (struct breakpoint *)value;
struct task *leader = (struct task *)data; 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) static int disable_nonlocked_bp_cb(unsigned long key, const void *value, void *data)
{ {
(void)key;
struct breakpoint *bp = (struct breakpoint *)value; struct breakpoint *bp = (struct breakpoint *)value;
struct task *leader = (struct task *)data; struct task *leader = (struct task *)data;
@ -567,7 +588,6 @@ void breakpoint_disable_all_nonlocked(struct task *leader)
if (leader->breakpoints) if (leader->breakpoints)
dict_apply_to_all(leader->breakpoints, disable_nonlocked_bp_cb, leader); 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) 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 breakpoint *bp = (struct breakpoint *)value;
struct task *leader = (struct task *)data; struct task *leader = (struct task *)data;
(void)key;
debug(DEBUG_FUNCTION, "pid=%d", leader->pid); debug(DEBUG_FUNCTION, "pid=%d", leader->pid);
breakpoint_enable(leader, bp); 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 breakpoint *bp = (struct breakpoint *)value;
struct task *leader = (struct task *)data; struct task *leader = (struct task *)data;
(void)key;
debug(DEBUG_FUNCTION, "pid=%d", leader->pid); debug(DEBUG_FUNCTION, "pid=%d", leader->pid);
breakpoint_disable(leader, bp); breakpoint_disable(leader, bp);
@ -608,7 +632,30 @@ void breakpoint_disable_all(struct task *leader)
if (leader->breakpoints) if (leader->breakpoints)
dict_apply_to_all(leader->breakpoints, disable_bp_cb, leader); 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) 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 breakpoint *bp = (struct breakpoint *)value;
struct task *leader = (struct task *)data; struct task *leader = (struct task *)data;
(void)key;
breakpoint_delete(leader, bp); breakpoint_delete(leader, bp);
return 0; return 0;
} }
@ -624,7 +673,9 @@ void breakpoint_clear_all(struct task *leader)
{ {
if (leader->breakpoints) { if (leader->breakpoints) {
dict_apply_to_all(leader->breakpoints, &destroy_breakpoint_cb, leader); dict_apply_to_all(leader->breakpoints, &destroy_breakpoint_cb, leader);
#if HW_BREAKPOINTS > 0
assert(leader->hw_bp_num == 0); assert(leader->hw_bp_num == 0);
#endif
dict_clear(leader->breakpoints); dict_clear(leader->breakpoints);
leader->breakpoints = NULL; 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; struct task *new_task = (struct task *)data;
size_t ext = bp->ext; size_t ext = bp->ext;
(void)key;
if (bp->deleted) if (bp->deleted)
return 0; 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->enabled = bp->enabled;
new_bp->locked = bp->locked; new_bp->locked = bp->locked;
new_bp->hw = bp->hw; new_bp->hw = bp->hw;
new_bp->sw = bp->sw;
new_bp->type = bp->type; new_bp->type = bp->type;
new_bp->ext = ext; new_bp->ext = ext;
new_bp->refcnt = 1; new_bp->refcnt = 1;
@ -695,7 +749,8 @@ static int clone_single_cb(unsigned long key, const void *value, void *data)
} }
else else
#endif #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) if (ext)
memcpy((void *)new_bp + ext, (void *)bp + ext, ext); memcpy((void *)new_bp + ext, (void *)bp + ext, ext);
@ -732,6 +787,8 @@ int breakpoint_put(struct breakpoint *bp)
if (--bp->refcnt) if (--bp->refcnt)
return 0; return 0;
assert(bp->enabled == 0);
free(bp); free(bp);
} }
return 1; return 1;

View File

@ -50,6 +50,7 @@ struct breakpoint {
unsigned int enabled:1; unsigned int enabled:1;
unsigned int locked:1; unsigned int locked:1;
unsigned int deleted:1; unsigned int deleted:1;
unsigned int sw:1;
unsigned int hw:1; unsigned int hw:1;
unsigned int was_hw:1; unsigned int was_hw:1;
unsigned int break_insn:1; unsigned int break_insn:1;
@ -69,9 +70,6 @@ struct breakpoint {
/* setup the basic breakpoint support for a given leader */ /* setup the basic breakpoint support for a given leader */
void breakpoint_setup(struct task *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. */ /* get a new breakpoint structure. */
struct breakpoint *breakpoint_new(struct task *task, arch_addr_t addr, struct library_symbol *libsym, int type); 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_enable_all(struct task *leader);
void breakpoint_disable_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_enable_all_nonlocked(struct task *leader);
void breakpoint_disable_all_nonlocked(struct task *leader); void breakpoint_disable_all_nonlocked(struct task *leader);
void breakpoint_clear_all(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 #else
static inline void enable_scratch_hw_bp(struct task *task, struct breakpoint *bp) 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) static inline void disable_scratch_hw_bp(struct task *task, struct breakpoint *bp)
{ {
(void)task;
(void)bp;
} }
#endif #endif
@ -120,10 +123,12 @@ void breakpoint_hw_destroy(struct task *task);
#else #else
static inline void breakpoint_hw_clone(struct task *task) static inline void breakpoint_hw_clone(struct task *task)
{ {
(void)task;
} }
static inline void breakpoint_hw_destroy(struct task *task) static inline void breakpoint_hw_destroy(struct task *task)
{ {
(void)task;
} }
#endif #endif

View File

@ -151,7 +151,7 @@ struct rb_sym *bin_file_lookup(struct bin_file *binfile, bfd_vma addr, unsigned
if (!name || !*name) if (!name || !*name)
name = "?"; name = "?";
else { 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) if (alloc)
name = alloc; name = alloc;
} }
@ -289,10 +289,9 @@ void bin_file_sym_put(struct rb_sym *sym)
if (!--sym->refcnt) { if (!--sym->refcnt) {
free(sym->sym); free(sym->sym);
if (!binfile) if (binfile)
return; rb_erase(&sym->node, &binfile->sym_table);
free(sym);
rb_erase(&sym->node, &binfile->sym_table);
} }
bin_file_put(binfile); bin_file_put(binfile);
} }

View File

@ -364,8 +364,10 @@ static void client_remove_process(struct process *process)
{ {
process = pid_rb_delete(&pid_table, process->pid); process = pid_rb_delete(&pid_table, process->pid);
if (process) if (process) {
process_reset(process);
free(process); free(process);
}
} }
@ -487,14 +489,15 @@ static int client_func(void)
process_about_exit(process); process_about_exit(process);
break; break;
case MT_EXIT: case MT_EXIT:
process_exit(process); if (process_exit(process))
client_remove_process(process);
break; break;
case MT_NOFOLLOW: case MT_NOFOLLOW:
process_reset(process);
client_remove_process(process); client_remove_process(process);
break; break;
case MT_SCAN: 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; break;
case MT_ADD_MAP: case MT_ADD_MAP:
process_add_map(process, payload, mt_msg.payload_len); 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); process_del_map(process, payload, mt_msg.payload_len);
break; break;
case MT_DETACH: case MT_DETACH:
process_detach(process); if (process_detach(process))
client_remove_process(process);
break; break;
default: default:
fatal("protocol violation 0x%08x", mt_msg.operation); fatal("protocol violation 0x%08x", mt_msg.operation);
@ -659,8 +663,7 @@ static void signal_exit(int sig)
signal(SIGINT, SIG_IGN); signal(SIGINT, SIG_IGN);
signal(SIGTERM, 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) static int scan_process(struct process *process)
@ -761,6 +764,8 @@ int client_start(void)
void *client_thread(void *unused) void *client_thread(void *unused)
{ {
(void)unused;
if (options.interactive) { if (options.interactive) {
ioevent_add_input(client_fd, client_func); ioevent_add_input(client_fd, client_func);
@ -824,6 +829,7 @@ int client_stop(void)
{ {
if (thread) { if (thread) {
thread_join(thread); thread_join(thread);
free(thread);
thread = NULL; thread = NULL;
} }

View File

@ -424,6 +424,7 @@ static void stack_put(struct rb_stack *stack_node)
for(i = 0; i < stack->entries; ++i) for(i = 0; i < stack->entries; ++i)
bin_file_sym_put(stack->syms[i]); bin_file_sym_put(stack->syms[i]);
free(stack->addrs);
free(stack->syms); free(stack->syms);
} }
free(stack); free(stack);
@ -479,7 +480,7 @@ static struct rb_stack *stack_clone(struct process *process, struct rb_stack *st
return this; 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_root *root = &process->stack_table;
struct rb_node **new = &(root->rb_node), *parent = NULL; 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); uint64_t size = process->val64(mt_map->size);
struct list_head *it; struct list_head *it;
(void)payload_len;
list_for_each(it, &process->map_list) { list_for_each(it, &process->map_list) {
struct map *map = container_of(it, struct 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) static int skip_none(struct rb_stack *stack)
{ {
(void)stack;
return 0; 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); 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 int new = 0;
unsigned long n = payload_len / process->ptr_size; 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_printf("leaks total: %lu\n", process->leaks);
dump_flush(); dump_flush();
if (!options.interactive) if (!options.interactive) {
process_dump_sortby(process); process_dump_sortby(process);
return 1;
}
return leaks; return 0;
} }
static inline unsigned long roundup_mask(unsigned long val, unsigned long mask) 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 (stack_size) {
if (!is_sane(block, mt_msg->operation)) { 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->n_mismatched++;
stack->tsc = process->tsc++; stack->tsc = process->tsc++;
@ -1462,7 +1469,7 @@ void process_free(struct process *process, struct mt_msg *mt_msg, void *payload)
} }
if (stack_size) { 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->n_badfree++;
stack->tsc = process->tsc++; stack->tsc = process->tsc++;
@ -1477,6 +1484,8 @@ void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void *
unsigned int pid; unsigned int pid;
struct list_head *it; struct list_head *it;
(void)mt_msg;
if (!process->tracing) if (!process->tracing)
return; return;
@ -1508,7 +1517,7 @@ void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void *
} }
if (unlikely(options.kill)) { 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(); abort();
} }
return; return;
@ -1562,7 +1571,7 @@ void process_alloc(struct process *process, struct mt_msg *mt_msg, void *payload
process_rb_delete_block(process, block); 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)) { if (process_rb_insert_block(process, ptr, size, stack, 0, mt_msg->operation)) {
fprintf(stderr, "process_rb_insert_block failed\n"); 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); process_set_status(process, MT_PROCESS_EXIT);
if (!options.interactive) if (!options.interactive) {
process_dump_sortby(process); process_dump_sortby(process);
else return 1;
fprintf(stderr, "+++ process %d exited +++\n", process->pid); }
fprintf(stderr, "+++ process %d exited\n", process->pid);
return 0;
} }
void process_about_exit(struct process *process) 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); 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); process_set_status(process, MT_PROCESS_DETACH);
if (options.auto_scan) if (options.auto_scan) {
process_leaks_scan(process, SCAN_ALL); process_leaks_scan(process, SCAN_ALL);
else }
if (!options.interactive) else {
process_dump_sortby(process); if (!options.interactive) {
process_dump_sortby(process);
ret = 1;
}
}
client_send_msg(process, MT_DETACH, NULL, 0); client_send_msg(process, MT_DETACH, NULL, 0);
return ret;
} }
void process_set_status(struct process *process, enum process_status status) void process_set_status(struct process *process, enum process_status status)

View File

@ -90,15 +90,15 @@ void process_set_status(struct process *process, enum process_status status);
void process_start_input(struct process *process); void process_start_input(struct process *process);
void process_stop_input(struct process *process); void process_stop_input(struct process *process);
void process_about_exit(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_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_alloc(struct process *process, struct mt_msg *msg, void *payload);
void process_free(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_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_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_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); void process_realloc_done(struct process *process, struct mt_msg *mt_msg, void *payload);
unsigned long process_leaks_scan(struct process *process, int mode); unsigned long process_leaks_scan(struct process *process, int mode);

View File

@ -90,35 +90,35 @@ static const char *outfile;
static int quit; static int quit;
static struct cmd_opt dump_opts[] = { static struct cmd_opt dump_opts[] = {
{ "allocations", 2, process_dump_sort_allocations, "sort by number of open allocations" }, { "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)" }, { "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" }, { "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" }, { "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" }, { "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" }, { "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" }, { "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" }, { "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" }, { "tsc", 2, process_dump_sort_tsc, "sort by time stamp counter", NULL, NULL },
{ "usage", 1, process_dump_sort_usage, "sort by number of bytes" }, { "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, 0, NULL, "\n use > to dump the output into a file", NULL, NULL },
}; };
static struct cmd_opt set_opts[] = { static struct cmd_opt set_opts[] = {
{ "searchpath", 1, do_set_searchpath, "set searchpath for binaries and libraries" }, { "searchpath", 1, do_set_searchpath, "set searchpath for binaries and libraries", NULL, NULL },
{ "depth", 1, do_set_depth, "set backtrace depth" }, { "depth", 1, do_set_depth, "set backtrace depth", NULL, NULL },
{ }, { },
}; };
static struct cmd_opt show_opts[] = { static struct cmd_opt show_opts[] = {
{ "info", 1, do_show_info, "show client settings" }, { "info", 1, do_show_info, "show client settings", NULL, NULL },
{ "searchpath", 1, do_show_searchpath, "show searchpath for binaries and libraries" }, { "searchpath", 1, do_show_searchpath, "show searchpath for binaries and libraries", NULL, NULL },
{ }, { },
}; };
static struct cmd_opt scan_opts[] = { static struct cmd_opt scan_opts[] = {
{ "all", 1, (void *)SCAN_ALL, "scan all memory blocks" }, { "all", 1, (void *)SCAN_ALL, "scan all memory blocks", NULL, NULL },
{ "leak", 1, (void *)SCAN_LEAK, "scan only leaked allocations" }, { "leak", 1, (void *)SCAN_LEAK, "scan only leaked allocations", NULL, NULL },
{ "new", 1, (void *)SCAN_NEW, "scan only allocations since last scan" }, { "new", 1, (void *)SCAN_NEW, "scan only allocations since last scan", NULL, NULL },
{ }, { },
}; };
@ -144,21 +144,24 @@ static struct cmd_opt cmds[] = {
1, 1,
do_proclist, do_proclist,
"list processes", "list processes",
"" "",
NULL
}, },
{ {
quit_str, quit_str,
1, 1,
do_quit, do_quit,
"exit the program", "exit the program",
"" "",
NULL
}, },
{ {
reset_str, reset_str,
1, 1,
do_reset, do_reset,
"reset all current memory allocation", "reset all current memory allocation",
"[<pid>]" "[<pid>]",
NULL
}, },
{ {
scan_str, scan_str,
@ -189,21 +192,24 @@ static struct cmd_opt cmds[] = {
4, 4,
do_start, do_start,
"start allocation tracing", "start allocation tracing",
"" "",
NULL
}, },
{ {
status_str, status_str,
4, 4,
do_status, do_status,
"show allocation status", "show allocation status",
"[<pid>]" "[<pid>]",
NULL
}, },
{ {
stop_str, stop_str,
3, 3,
do_stop, do_stop,
"stop allocation tracing", "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[]) static int do_show_info(struct cmd_opt *cmd, struct cmd_opt *opt, int argc, const char *argv[])
{ {
(void)argv;
if (argc > 2) { if (argc > 2) {
fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name); fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name);
return -1; 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; struct opt_b_t *p = options.opt_b;
(void)argv;
if (argc > 3) { if (argc > 3) {
fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name); fprintf(stderr, "%s: too many option argument for '%s'\n", cmd->name, opt->name);
return -1; 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[]) static int do_proclist(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
(void)cmd;
(void)argv;
if (argc > 1) { if (argc > 1) {
fprintf(stderr, "%s: expect no arguments\n", proclist_str); fprintf(stderr, "%s: expect no arguments\n", proclist_str);
return -1; 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[]) static int do_quit(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
(void)cmd;
(void)argc;
(void)argv;
quit = 1; quit = 1;
return 0; return 0;
@ -683,6 +700,9 @@ static int do_reset(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
struct process *process; struct process *process;
(void)cmd;
(void)argc;
process = get_process(argv[1]); process = get_process(argv[1]);
if (!process) if (!process)
return -1; return -1;
@ -702,6 +722,8 @@ static int do_scan(struct cmd_opt *cmd, int argc, const char *argv[])
unsigned int i; unsigned int i;
int mode; int mode;
(void)argc;
if (!client_connected()) if (!client_connected())
return -1; return -1;
@ -798,6 +820,9 @@ static int do_start(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
struct process *process; struct process *process;
(void)cmd;
(void)argc;
if (!client_connected()) if (!client_connected())
return -1; return -1;
@ -819,6 +844,9 @@ static int do_status(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
struct process *process; struct process *process;
(void)cmd;
(void)argc;
process = get_process(argv[1]); process = get_process(argv[1]);
if (!process) if (!process)
return -1; return -1;
@ -832,6 +860,9 @@ static int do_stop(struct cmd_opt *cmd, int argc, const char *argv[])
{ {
struct process *process; struct process *process;
(void)cmd;
(void)argc;
if (!client_connected()) if (!client_connected())
return -1; return -1;

View File

@ -1,6 +1,13 @@
#define PACKAGE_VERSION "@MT_VERSION_STRING@" #define PACKAGE_VERSION "@MT_VERSION_STRING@"
#ifndef HAVE_PROCESS_VM_READV
#cmakedefine 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 #cmakedefine DISABLE_CLIENT
#endif

View File

@ -47,6 +47,9 @@ void _debug(int level, const char *file, const char *function, int line, const c
va_end(args); va_end(args);
fprintf(stderr, "DEBUG: %s():%s@%d - %s\n", function, file, line, buf); fprintf(stderr, "DEBUG: %s():%s@%d - %s\n", function, file, line, buf);
// fflush(debug_file);
free(buf);
} }
#endif #endif

View File

@ -26,6 +26,8 @@
#include "config.h" #include "config.h"
#define DEBUG
/* debug levels: /* debug levels:
*/ */
enum { enum {

View File

@ -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); set_reg(rs_current, regnum, DWARF_WHERE_REG, n);
break; break;
case DW_CFA_remember_state: 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)); memcpy(&rs_tmp->store, rs_current, regs_size(as));
rs_tmp->next = rs_stack; rs_tmp->next = rs_stack;
rs_stack = rs_tmp; rs_stack = rs_tmp;

284
event.c
View File

@ -53,10 +53,16 @@
#include "timer.h" #include "timer.h"
#include "trace.h" #include "trace.h"
#define RET_DELETED 1
#define RET_DEFERED 2
static LIST_HEAD(event_head); static LIST_HEAD(event_head);
void queue_event(struct task *task) void queue_event(struct task *task)
{ {
assert(task->event.type != EVENT_NONE);
assert(task->stopped);
if (task) { if (task) {
if (task->event.type != EVENT_NONE) if (task->event.type != EVENT_NONE)
list_add_tail(&task->event.list, &event_head); list_add_tail(&task->event.list, &event_head);
@ -88,52 +94,47 @@ void init_event(struct task *task)
INIT_LIST_HEAD(&task->event.list); 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) { switch(type) {
case EVENT_FORK: case EVENT_FORK:
str = "fork"; return "fork";
break;
case EVENT_VFORK: case EVENT_VFORK:
str = "vfork"; return "vfork";
break;
case EVENT_CLONE: case EVENT_CLONE:
str = "clone"; return "clone";
break;
default: default:
str = "?";
break; 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; debug(DEBUG_EVENT, "+++ process %s pid=%d, newpid=%d", get_clone_type(task->event.type), task->pid, newtask->pid);
int newpid = task->event.e_un.newpid;
debug(DEBUG_FUNCTION, "pid=%d, newpid=%d", task->pid, newpid);
if (unlikely(options.verbose)) 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->event.type != EVENT_NEW)
if (!newtask) fprintf(stderr, "!!!task new unexpected event for pid=%d: %d\n", newtask->pid, newtask->event.type);
goto fail; 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 (newtask->leader == newtask) {
if (!options.follow) {
untrace_proc(newtask);
return RET_DELETED;
}
if (task_fork(task, newtask) < 0) if (task_fork(task, newtask) < 0)
goto fail; goto fail;
if (!options.follow) {
remove_proc(newtask);
return;
}
report_fork(newtask, task); report_fork(newtask, task);
} }
else { else {
@ -141,77 +142,130 @@ static void handle_clone(struct task *task, enum event_type type)
goto fail; goto fail;
} }
continue_task(newtask, newtask->event.e_un.signum); newtask->is_new = 0;
return continue_task(newtask, 0);
return;
fail: fail:
fprintf(stderr, fprintf(stderr, "Error during clone of pid=%d - This process won't be traced!\n", newtask->pid);
"Error during init of tracing process %d\n" return -1;
"This process won't be traced.\n",
newpid
);
} }
static void handle_signal(struct task *task) static int do_clone_cb(struct task *newtask, void *data)
{ {
if (unlikely(options.verbose > 1)) { int ret;
if (task->event.e_un.signum && (task->event.e_un.signum != SIGSTOP || !task->was_stopped)) struct task *task = data;
fprintf(stderr, "+++ process pid=%d signal %d: %s +++\n", task->pid, task->event.e_un.signum, strsignal(task->event.e_un.signum));
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) static void show_exit(struct task *task)
{ {
if (unlikely(options.verbose)) 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 (task->leader == task) {
if (!options.logfile && report_about_exit(task) != -1) { if (!options.logfile && report_about_exit(task) != -1) {
task->about_exit = 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); show_exit(task);
if (task->leader == task) { if (task->leader == task) {
report_exit(task); report_exit(task);
remove_proc(task); untrace_proc(task);
} }
else { else {
remove_task(task); 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)) 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) { if (task->leader == task) {
report_exit(task); report_exit(task);
remove_proc(task); untrace_proc(task);
} }
else { else {
remove_task(task); 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)) 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) if (!options.follow_exec)
goto nofollow; goto nofollow;
@ -221,18 +275,20 @@ static void handle_exec(struct task *task)
goto untrace; goto untrace;
} }
continue_task(task, 0); return continue_task(task, 0);
return;
nofollow: nofollow:
report_nofollow(task); report_nofollow(task);
untrace: untrace:
remove_proc(task); untrace_proc(task);
return RET_DELETED;
} }
static int handle_call_after(struct task *task, struct breakpoint *bp) static int handle_call_after(struct task *task, struct breakpoint *bp)
{ {
struct timespec start; struct timespec start;
(void)bp;
if (!task->breakpoint) if (!task->breakpoint)
return 0; return 0;
@ -255,16 +311,41 @@ static int handle_call_after(struct task *task, struct breakpoint *bp)
return 0; return 0;
} }
static void handle_breakpoint(struct task *task) static int handle_breakpoint(struct task *task)
{ {
struct breakpoint *bp = task->event.e_un.breakpoint; struct breakpoint *bp = task->event.e_un.breakpoint;
unsigned int hw = bp->hw; 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)) if (unlikely(options.verbose > 1))
set_timer(&task->halt_time, hw ? &hw_bp_time : &sw_bp_time); 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 HW_BREAKPOINTS > 1
if (bp->type >= BP_HW) { if (bp->type >= BP_HW) {
if (unlikely(++bp->hwcnt >= (BP_REORDER_THRESHOLD << hw))) { if (unlikely(++bp->hwcnt >= (BP_REORDER_THRESHOLD << hw))) {
@ -281,28 +362,7 @@ static void handle_breakpoint(struct task *task)
} }
#endif #endif
if (unlikely(options.verbose)) if (bp->on_hit && bp->on_hit(task, bp)) {
++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))) {
continue_task(task, 0); continue_task(task, 0);
goto end; 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: end:
breakpoint_put(bp); breakpoint_put(bp);
return 0;
} }
int handle_event(void) int handle_event(struct task *task)
{ {
struct task *task = next_event(); int ret;
if (!task) if (!task)
return 0; 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; struct event *event = &task->event;
enum event_type type = event->type; enum event_type type = event->type;
event->type = EVENT_NONE;
debug(DEBUG_FUNCTION, "pid=%d, type=%d", task->pid, event->type);
switch (type) { switch (type) {
case EVENT_NONE: case EVENT_NONE:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event none", task->pid); ret = continue_task(task, task->event.e_un.signum);
break; break;
case EVENT_SIGNAL: case EVENT_SIGNAL:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event signal %d", task->pid, event->e_un.signum); ret = handle_signal(task);
handle_signal(task);
break; break;
case EVENT_ABOUT_EXIT: case EVENT_ABOUT_EXIT:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit %d", task->pid, event->e_un.ret_val); ret = handle_about_exit(task);
handle_about_exit(task); goto out;
break;
case EVENT_EXIT: case EVENT_EXIT:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit %d", task->pid, event->e_un.ret_val); ret = handle_exit(task);
handle_exit(task);
break; break;
case EVENT_EXIT_SIGNAL: case EVENT_EXIT_SIGNAL:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event exit signal %d", task->pid, event->e_un.signum); ret = handle_exit_signal(task);
handle_exit_signal(task);
break; break;
case EVENT_FORK: case EVENT_FORK:
case EVENT_VFORK: case EVENT_VFORK:
case EVENT_CLONE: case EVENT_CLONE:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event clone (%u)", task->pid, event->e_un.newpid); ret = handle_child(task);
handle_clone(task, type);
break; break;
case EVENT_EXEC: case EVENT_EXEC:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event exec()", task->pid); ret = handle_exec(task);
handle_exec(task);
break; break;
case EVENT_BREAKPOINT: case EVENT_BREAKPOINT:
debug(DEBUG_EVENT_HANDLER, "pid=%d, event breakpoint %#lx", task->pid, event->e_un.breakpoint->addr); ret = handle_breakpoint(task);
handle_breakpoint(task); goto out;
case EVENT_NEW:
ret = handle_new(task);
break; break;
default: default:
fprintf(stderr, "Error! unknown event?\n"); fprintf(stderr, "!!!pid=%d, unknown event %d\n", task->pid, type);
return -1; 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;
} }

View File

@ -36,7 +36,8 @@ enum event_type {
EVENT_CLONE, EVENT_CLONE,
EVENT_VFORK, EVENT_VFORK,
EVENT_EXEC, EVENT_EXEC,
EVENT_BREAKPOINT EVENT_BREAKPOINT,
EVENT_NEW,
}; };
struct event { struct event {
@ -54,7 +55,7 @@ void init_event(struct task *task);
void remove_event(struct task *task); void remove_event(struct task *task);
struct task *next_event(void); struct task *next_event(void);
void queue_event(struct task *task); void queue_event(struct task *task);
int handle_event(void); int handle_event(struct task *task);
#endif #endif

View File

@ -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 library *lib = container_of(it, struct library, list);
struct libref *libref = lib->libref; 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)) 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); library_delete(leader, lib);
} }
@ -274,7 +274,7 @@ static struct library *_library_add(struct task *leader, struct libref *libref)
insert_lib(leader, lib); insert_lib(leader, lib);
if (unlikely(options.verbose > 1)) 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; return lib;
} }

24
list.h
View File

@ -55,41 +55,39 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
* This is only for internal list manipulation where we know * This is only for internal list manipulation where we know
* the prev/next entries already! * the prev/next entries already!
*/ */
static inline void __list_add(struct list_head *new, static inline void __list_add(struct list_head *elem, struct list_head *prev, struct list_head *next)
struct list_head *prev,
struct list_head *next)
{ {
next->prev = new; next->prev = elem;
new->next = next; elem->next = next;
new->prev = prev; elem->prev = prev;
prev->next = new; prev->next = elem;
} }
/** /**
* list_add - add a new entry * 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 * @head: list head to add it after
* *
* Insert a new entry after the specified head. * Insert a new entry after the specified head.
* This is good for implementing stacks. * 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 * 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 * @head: list head to add it before
* *
* Insert a new entry before the specified head. * Insert a new entry before the specified head.
* This is useful for implementing queues. * 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);
} }
/* /*

19
main.c
View File

@ -61,6 +61,8 @@ struct mt_timer report_in_time;
struct mt_timer report_out_time; struct mt_timer report_out_time;
struct mt_timer skip_bp_time; struct mt_timer skip_bp_time;
pid_t mtrace_pid;
static int do_exit; static int do_exit;
void mtrace_request_exit(void) void mtrace_request_exit(void)
@ -69,7 +71,7 @@ void mtrace_request_exit(void)
return; return;
if (unlikely(options.verbose)) if (unlikely(options.verbose))
fprintf(stderr, "+++ request exit +++\n"); fprintf(stderr, "+++ request exit\n");
do_exit = 1; do_exit = 1;
wait_event_wakeup(); wait_event_wakeup();
@ -110,6 +112,8 @@ static void mtrace_init(char **cmd_args)
{ {
struct opt_p_t *opt_p_tmp; struct opt_p_t *opt_p_tmp;
mtrace_pid = getpid();
if (options.command) { if (options.command) {
struct task *task = task_create(cmd_args); struct task *task = task_create(cmd_args);
@ -117,7 +121,7 @@ static void mtrace_init(char **cmd_args)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
if (unlikely(options.verbose)) 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) 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) static void mtrace_main(void)
{ {
struct task *task;
while(!do_exit) { while(!do_exit) {
if (task_list_empty()) if (task_list_empty())
break; break;
if (handle_event() == -1) task = next_event();
break; if (task) {
if (handle_event(task) == -1)
break;
}
if (server_poll() == -1) if (server_poll() == -1)
break; break;
@ -142,8 +151,6 @@ int main(int argc, char *argv[])
{ {
char **cmd_args = process_options(argc, argv); char **cmd_args = process_options(argc, argv);
init_pid_hash();
if (options.trace) { if (options.trace) {
if (options.logfile) { if (options.logfile) {
if (server_logfile() == -1) if (server_logfile() == -1)

3
main.h
View File

@ -23,6 +23,8 @@
#ifndef _INC_MAIN_H #ifndef _INC_MAIN_H
#define _INC_MAIN_H #define _INC_MAIN_H
#include <sys/types.h>
#include "timer.h" #include "timer.h"
void mtrace_request_exit(void); void mtrace_request_exit(void);
@ -36,5 +38,6 @@ struct mt_timer report_in_time;
struct mt_timer report_out_time; struct mt_timer report_out_time;
struct mt_timer skip_bp_time; struct mt_timer skip_bp_time;
pid_t mtrace_pid;
#endif #endif

15
mtelf.c
View File

@ -259,7 +259,7 @@ static int elf_lib_init(struct mt_elf *mte, struct task *task, struct libref *li
} }
#endif #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) 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; return -1;
} }
@ -474,7 +474,7 @@ static int entry_breakpoint_on_hit(struct task *task, struct breakpoint *a)
return 1; 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]; char fname[PATH_MAX];
int ret; int ret;
@ -520,7 +520,7 @@ struct libref *elf_read_main_binary(struct task *task)
close_elf(&mte); close_elf(&mte);
report_attach(task); report_attach(task, was_attached);
library_add(task, libref); library_add(task, libref);
@ -529,7 +529,10 @@ struct libref *elf_read_main_binary(struct task *task)
struct mt_elf mte_ld = { }; 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)) { if (!elf_read(&mte_ld, task, fname, (GElf_Addr)base)) {
struct libref *libref; struct libref *libref;
@ -547,7 +550,7 @@ struct libref *elf_read_main_binary(struct task *task)
if (!ret) { if (!ret) {
library_add(task, libref); 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); arch_addr_t addr = find_solib_break(&mte_ld);
if (!addr) if (!addr)
addr = ARCH_ADDR_T(entry); addr = ARCH_ADDR_T(entry);
@ -562,7 +565,7 @@ struct libref *elf_read_main_binary(struct task *task)
else { else {
entry_bp->breakpoint.on_hit = entry_breakpoint_on_hit; entry_bp->breakpoint.on_hit = entry_breakpoint_on_hit;
entry_bp->breakpoint.locked = 1; 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); breakpoint_enable(task, &entry_bp->breakpoint);
} }

View File

@ -53,15 +53,10 @@ struct mt_elf {
GElf_Addr pltgot; 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); int elf_read_library(struct task *task, struct libref *libref, const char *filename, GElf_Addr bias);
/* Create a library object representing the main binary. */ /* 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 #endif

View File

@ -39,6 +39,7 @@
#include <pwd.h> #include <pwd.h>
#include "common.h" #include "common.h"
#include "debug.h"
#include "options.h" #include "options.h"
#ifndef SYSCONFDIR #ifndef SYSCONFDIR
@ -171,11 +172,11 @@ static char *search_for_command(char *filename)
static int add_opt_F(char *filename) static int add_opt_F(char *filename)
{ {
struct opt_F_t *tmp = malloc(sizeof(*tmp));
if (access(filename, R_OK)) if (access(filename, R_OK))
return -1; return -1;
struct opt_F_t *tmp = malloc(sizeof(*tmp));
if (!tmp) { if (!tmp) {
fprintf(stderr, "%s\n", strerror(errno)); fprintf(stderr, "%s\n", strerror(errno));
exit(1); exit(1);

View File

@ -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)); 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)); 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); _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) static void _report_new(struct task *task, struct library_symbol *libsym)
{ {
_report_alloc_op(task, libsym, options.sanity ? MT_NEW : MT_MALLOC); _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) 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); unsigned long ret = fetch_retval(task);
if (ret) { if (!addr) {
unsigned long size = fetch_param(task, 1); if (ret)
report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym);
report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym); return;
} }
if (fetch_param(task, 0)) { if (ret)
if (task_is_64bit(task)) { report_alloc(task, MT_REALLOC, ret, size, options.bt_depth, libsym);
struct mt_alloc_payload_64 *alloc = alloca(sizeof(*alloc));
alloc->ptr = (uint64_t)ret; if (task_is_64bit(task)) {
alloc->size = (uint64_t)task->pid; struct mt_alloc_payload_64 *alloc = alloca(sizeof(*alloc));
server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc)); alloc->ptr = (uint64_t)ret;
} alloc->size = (uint64_t)task->pid;
else {
struct mt_alloc_payload_32 *alloc = alloca(sizeof(*alloc));
alloc->ptr = (uint32_t)ret; server_send_msg(MT_REALLOC_DONE, task->leader->pid, alloc, sizeof(*alloc));
alloc->size = (uint32_t)task->pid; }
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); 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) 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 }, { "pvalloc", "pvalloc", 1, NULL, _report_pvalloc },
{ "mremap", "mremap", 0, report_mremap, _report_mremap }, { "mremap", "mremap", 0, report_mremap, _report_mremap },
{ "cfree", "cfree", 1, report_free, NULL }, { "cfree", "cfree", 1, report_free, NULL },
{ "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)", "_Znwj", 1, NULL, _report_new },
{ "new[](unsigned int)", "_Znaj", 1, NULL, _report_new_array }, { "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&)", "_ZnwmRKSt9nothrow_t", 1, NULL, _report_new },
{ "new[](unsigned long, std::nothrow_t const&)", "_ZnamRKSt9nothrow_t", 1, NULL, _report_new_array }, { "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*)", "_ZdlPv", 1, report_delete, NULL },
{ "delete[](void*)", "_ZdaPv", 1, report_delete_array, 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&)", "_ZdlPvRKSt9nothrow_t", 1, report_delete, NULL },
{ "delete[](void*, std::nothrow_t const&)", "_ZdaPvRKSt9nothrow_t", 1, report_delete_array, 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)", "_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*, 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) 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); 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()) if (!server_connected())
return -1; return -1;
@ -530,7 +575,7 @@ static void report_process(struct task *leader)
{ {
struct list_head *it; struct list_head *it;
report_attach(leader); report_attach(leader, 1);
list_for_each(it, &leader->libraries_list) { list_for_each(it, &leader->libraries_list) {
struct library *lib = container_of(it, struct library, list); struct library *lib = container_of(it, struct library, list);

View File

@ -23,6 +23,8 @@
#ifndef _INC_REPORT_H #ifndef _INC_REPORT_H
#define _INC_REPORT_H #define _INC_REPORT_H
#include <unistd.h>
#include "forward.h" #include "forward.h"
struct function { 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_del_map(struct task *task, struct library *lib);
int report_info(int do_trace); int report_info(int do_trace);
int report_scan(pid_t pid, const void *data, unsigned int data_len); 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_fork(struct task *task, struct task *ptask);
int report_exit(struct task *task); int report_exit(struct task *task);
int report_about_exit(struct task *task); int report_about_exit(struct task *task);

View File

@ -241,6 +241,8 @@ static void *server_listen_thread(void *ptr)
{ {
int ret; int ret;
(void)ptr;
for(;;) { for(;;) {
if (!server_connected()) { if (!server_connected()) {
thread_enable_cancel(); thread_enable_cancel();
@ -307,6 +309,8 @@ static void *server_pair_thread(void *ptr)
{ {
int ret; int ret;
(void)ptr;
for(;;) { for(;;) {
if (!server_connected()) if (!server_connected())
break; break;
@ -389,8 +393,11 @@ int server_stop(void)
if (listen_fd != -1) { if (listen_fd != -1) {
thread_cancel(thread); thread_cancel(thread);
if (thread) if (thread) {
thread_join(thread); thread_join(thread);
free(thread);
thread = NULL;
}
if (is_named(options.address)) if (is_named(options.address))
unlink(options.address); unlink(options.address);

View File

@ -63,6 +63,9 @@ struct map {
static void report_fault(int signo, siginfo_t* siginf, void* arg) 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)); fprintf(stderr, "fault signal %d (%s)\n", signo, strsignal(signo));
#ifndef DISABLE_CLIENT #ifndef DISABLE_CLIENT
@ -148,6 +151,8 @@ skip:
static void signal_exit(int sig) static void signal_exit(int sig)
{ {
(void)sig;
signal(SIGINT, SIG_IGN); signal(SIGINT, SIG_IGN);
signal(SIGTERM, 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) int os_init(void)
{ {
struct sigaction act; struct sigaction act;

View File

@ -489,6 +489,8 @@ static int rdebug_bp_on_hit(struct task *task, struct breakpoint *bp)
struct lt_r_debug_64 rdbg; struct lt_r_debug_64 rdbg;
struct task *leader = task->leader; struct task *leader = task->leader;
(void)bp;
debug(DEBUG_FUNCTION, "pid=%d", task->pid); debug(DEBUG_FUNCTION, "pid=%d", task->pid);
if (load_debug_struct(task, leader->os.debug_addr, &rdbg) < 0) 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 os_task_destroy(struct task *task)
{ {
(void)task;
} }
int os_task_clone(struct task *retp, struct task *task) int os_task_clone(struct task *retp, struct task *task)

View File

@ -52,34 +52,46 @@
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static volatile pid_t wakeup_pid = -1; 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; errno = 0;
return syscall(__NR_tgkill, task->leader->pid, task->pid, sig); 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) 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->stopped = 1;
task->leader->threads_stopped++; task->leader->threads_stopped++;
task->event.type = EVENT_SIGNAL; task->event.type = EVENT_NEW;
task->event.e_un.signum = 0; task->event.e_un.signum = 0;
if (!WIFSTOPPED(status)) { 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; return -1;
} }
stop_signal = WSTOPSIG(status); sig = WSTOPSIG(status);
if (stop_signal != signum) { if (sig != signum) {
task->event.e_un.signum = stop_signal; 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; return -1;
} }
@ -90,10 +102,8 @@ static int _trace_wait(struct task *task, int signum)
{ {
int status; int status;
if (unlikely(TEMP_FAILURE_RETRY(waitpid(task->pid, &status, __WALL)) != task->pid)) { if (unlikely(wait_task(task, &status) == -1))
fprintf(stderr, "%s pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno));
return -1; return -1;
}
if (WIFEXITED(status)) if (WIFEXITED(status))
return -1; return -1;
@ -103,11 +113,11 @@ static int _trace_wait(struct task *task, int signum)
int trace_wait(struct task *task) int trace_wait(struct task *task)
{ {
assert(task->attached == 0);
if (_trace_wait(task, SIGTRAP)) if (_trace_wait(task, SIGTRAP))
return -1; return -1;
queue_event(task);
return 0; return 0;
} }
@ -115,6 +125,8 @@ static int child_event(struct task *task, enum event_type ev)
{ {
unsigned long data; 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)) { if (unlikely(ptrace(PTRACE_GETEVENTMSG, task->pid, NULL, &data) == -1)) {
debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno)); debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno));
return -1; return -1;
@ -128,10 +140,7 @@ static int child_event(struct task *task, enum event_type ev)
if (unlikely(!child)) if (unlikely(!child))
return -1; return -1;
if (_trace_wait(child, SIGSTOP)) { child->attached = 1;
remove_task(child);
return -1;
}
} }
task->event.e_un.newpid = pid; 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) 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)) { 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.type = EVENT_EXIT_SIGNAL;
task->event.e_un.signum = WTERMSIG(status); 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; return 0;
} }
if (WIFEXITED(status)) { 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.type = EVENT_EXIT;
task->event.e_un.ret_val = WEXITSTATUS(status); 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; return 0;
} }
if (!WIFSTOPPED(status)) { if (!WIFSTOPPED(status)) {
/* should never happen */ fprintf(stderr, "!!!not WIFSTOPPED pid=%d\n", task->pid);
debug(DEBUG_EVENT, "NONE: pid=%d ???", task->pid);
return -1; 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) { switch(status >> 16) {
case 0:
break;
case PTRACE_EVENT_VFORK: 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); 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: 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); 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: 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); 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: case PTRACE_EVENT_EXEC:
task->event.type = EVENT_EXEC; task->event.type = EVENT_EXEC;
debug(DEBUG_EVENT, "EXEC: pid=%d", task->pid); debug(DEBUG_EVENT, "EXEC: pid=%d", task->pid);
@ -188,64 +212,77 @@ static int _process_event(struct task *task, int status)
{ {
unsigned long data; unsigned long data;
debug(DEBUG_EVENT, "ABOUT_EXIT: pid=%d", task->pid);
if (unlikely(ptrace(PTRACE_GETEVENTMSG, task->pid, NULL, &data) == -1)) { if (unlikely(ptrace(PTRACE_GETEVENTMSG, task->pid, NULL, &data) == -1)) {
debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno)); debug(DEBUG_EVENT, "PTRACE_GETEVENTMSG pid=%d %s", task->pid, strerror(errno));
return -1; return -1;
} }
task->event.e_un.ret_val = WEXITSTATUS(data); task->event.e_un.ret_val = WEXITSTATUS(data);
task->event.type = EVENT_ABOUT_EXIT; task->event.type = EVENT_ABOUT_EXIT;
debug(DEBUG_EVENT, "ABOUT_EXIT: pid=%d", task->pid);
return 0; return 0;
} }
default: default:
fprintf(stderr, "!!!PTRACE_EVENT_????? pid=%d %d\n", task->pid, status >> 16);
break; 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.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); debug(DEBUG_EVENT, "SIGNAL: pid=%d, signum=%d", task->pid, sig);
return stop_signal; 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 task *leader = task->leader;
struct breakpoint *bp = NULL; struct breakpoint *bp = NULL;
arch_addr_t ip; arch_addr_t ip;
int sig;
assert(task->stopped == 0);
assert(leader != NULL); assert(leader != NULL);
if (unlikely(options.verbose > 1)) if (unlikely(options.verbose > 1))
start_time(&task->halt_time); start_time(&task->halt_time);
task->stopped = 1;
leader->threads_stopped++; leader->threads_stopped++;
stop_signal = _process_event(task, status); sig = _process_event(task, status);
if (sig < 0) {
if (stop_signal == -1) {
fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__);
task->event.type = EVENT_NONE;
continue_task(task, 0); continue_task(task, 0);
return; return NULL;
} }
if (stop_signal == 0) if (task->event.type == EVENT_NONE) {
return; continue_task(task, task->event.e_un.signum);
return NULL;
}
if (unlikely(stop_signal != SIGTRAP)) if (unlikely(sig != SIGTRAP))
return; return task;
if (unlikely(fetch_context(task) == -1)) { if (unlikely(fetch_context(task) == -1)) {
fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__);
task->event.type = EVENT_NONE; task->event.type = EVENT_NONE;
continue_task(task, 0); continue_task(task, 0);
return; return NULL;
} }
ip = get_instruction_pointer(task); 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); bp = breakpoint_find(leader, ip - DECR_PC_AFTER_BREAK);
if (unlikely(!bp)) { if (unlikely(!bp)) {
fprintf(stderr, "%s:%d\n", __FUNCTION__, __LINE__); fprintf(stderr, "!!!%s: SIGTRAP pid=%d\n", __func__, task->pid);
// task->event.type = EVENT_NONE; return task;
// continue_task(task, 0);
return;
} }
#if HW_BREAKPOINTS > 0 #if HW_BREAKPOINTS > 0
assert(bp->type != BP_HW_SCRATCH); 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); debug(DEBUG_EVENT, "BREAKPOINT: pid=%d, addr=%#lx", task->pid, task->event.e_un.breakpoint->addr);
return; return task;
}
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
} }
void trace_me(void) 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) { #if 1
task->was_stopped = 0; if (signum == SIGSTOP)
signum = 0; 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; 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); assert(task->stopped);
debug(DEBUG_PROCESS, "pid=%d", task->pid);
if (!task->stopped)
return 0;
if (unlikely(ptrace(PTRACE_SETOPTIONS, task->pid, 0, (void *)0) == -1)) { if (unlikely(ptrace(PTRACE_SETOPTIONS, task->pid, 0, (void *)0) == -1)) {
if (errno != ESRCH) if (errno != ESRCH)
@ -351,45 +370,53 @@ int untrace_task(struct task *task, int signum)
goto skip; 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 (unlikely(ptrace(PTRACE_DETACH, task->pid, 0, sig) == -1)) {
if (task->traced) { if (errno != ESRCH)
if (errno != ESRCH) fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno));
fprintf(stderr, "PTRACE_DETACH pid=%d %s\n", task->pid, strerror(errno)); ret = -1;
ret = -1;
goto skip;
}
} }
task_kill(task, SIGCONT); task_kill(task, SIGCONT);
skip: skip:
task->leader->threads_stopped--; task->leader->threads_stopped--;
task->stopped = 0; task->stopped = 0;
task->was_stopped = 0; task->attached = 0;
task->traced = 0;
return ret; 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) int trace_attach(struct task *task)
{ {
debug(DEBUG_PROCESS, "pid=%d", task->pid); 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 (unlikely(ptrace(PTRACE_ATTACH, task->pid, 0, 0) == -1)) {
if (errno != ESRCH) if (errno != ESRCH)
fprintf(stderr, "PTRACE_ATTACH pid=%d %s\n", task->pid, strerror(errno)); fprintf(stderr, "PTRACE_ATTACH pid=%d %s\n", task->pid, strerror(errno));
trace_fail_warning();
return -1; return -1;
} }
if (_trace_wait(task, SIGSTOP)) if (_trace_wait(task, SIGSTOP))
return -1; return -1;
queue_event(task);
return 0; return 0;
} }
@ -409,15 +436,22 @@ int trace_set_options(struct task *task)
int continue_task(struct task *task, int signum) 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->leader != NULL);
assert(task->stopped); assert(task->stopped);
if (signum >= 0x80)
fprintf(stderr, "!!!signum >= 0x80 pid=%d: %d\n", task->pid, signum);
task->leader->threads_stopped--; task->leader->threads_stopped--;
task->stopped = 0; 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) if (errno != ESRCH)
fprintf(stderr, "PTRACE_CONT pid=%d %s\n", task->pid, strerror(errno)); fprintf(stderr, "PTRACE_CONT pid=%d %s\n", task->pid, strerror(errno));
return -1; return -1;
@ -427,10 +461,12 @@ int continue_task(struct task *task, int signum)
static void do_stop_cb(struct task *task, void *data) static void do_stop_cb(struct task *task, void *data)
{ {
(void)data;
if (task->stopped) if (task->stopped)
return; return;
task->was_stopped = 1; debug(DEBUG_EVENT, "task stop pid=%d", task->pid);
task_kill(task, SIGSTOP); task_kill(task, SIGSTOP);
} }
@ -441,6 +477,8 @@ void stop_threads(struct task *task)
assert(task->leader != NULL); assert(task->leader != NULL);
debug(DEBUG_EVENT, "stop threads pid=%d", task->pid);
if (leader->threads != leader->threads_stopped) { if (leader->threads != leader->threads_stopped) {
struct timespec start; struct timespec start;
@ -449,8 +487,12 @@ void stop_threads(struct task *task)
each_task(leader, &do_stop_cb, NULL); each_task(leader, &do_stop_cb, NULL);
while (leader->threads != leader->threads_stopped) while (leader->threads != leader->threads_stopped) {
queue_event(wait_event()); task = wait_event();
if (task)
queue_event(task);
}
if (unlikely(options.verbose > 1)) if (unlikely(options.verbose > 1))
set_timer(&start, &stop_time); 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 handle_singlestep(struct task *task, int (*singlestep)(struct task *task), struct breakpoint *bp)
{ {
int status; int status;
int stop_signal; int sig;
unsigned long ip;
for(;;) { assert(task->stopped);
if (unlikely(singlestep(task) == -1)) assert(task->skip_bp == NULL);
return -1; assert(bp->enabled == 0);
if (unlikely(TEMP_FAILURE_RETRY(waitpid(task->pid, &status, __WALL)) != task->pid)) { task->event.type = EVENT_NONE;
fprintf(stderr, "%s waitpid pid=%d %s\n", __FUNCTION__, task->pid, strerror(errno));
return 0;
}
stop_signal = _process_event(task, status); if (unlikely(singlestep(task) == -1)) {
fprintf(stderr, "!!!%s: single step failed pid=%d\n", __func__, task->pid);
if (stop_signal == -1) return -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(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 #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 (unlikely(ptrace(PTRACE_SINGLESTEP, task->pid, 0, 0) == -1)) {
if (errno != ESRCH) 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 -1;
} }
return 0; return 0;
@ -531,14 +574,10 @@ struct task *wait_event(void)
int status; int status;
int pid; int pid;
pid = waitpid(-1, &status, __WALL); pid = wait_task(NULL, &status);
if (unlikely(pid == -1)) { if (unlikely(pid == -1)) {
if (errno != EINTR) { if (errno == ECHILD)
if (errno == ECHILD) debug(DEBUG_EVENT, "No more traced programs");
debug(DEBUG_EVENT, "No more traced programs");
else
fprintf(stderr, "%s waitpid %s\n", __FUNCTION__, strerror(errno));
}
return NULL; return NULL;
} }
@ -558,12 +597,14 @@ struct task *wait_event(void)
if (likely(task)) if (likely(task))
trace_setup(task, status, SIGSTOP); trace_setup(task, status, SIGSTOP);
return NULL; return NULL;
} }
process_event(task, status); assert(!task->stopped);
task = process_event(task, status);
if (task)
assert(task->stopped);
return task; 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 != EFAULT) {
if (errno != ENOSYS) { 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; return -1;
} }
@ -755,10 +796,10 @@ ssize_t copy_str_from_proc(struct task *task, arch_addr_t addr, char *dst, size_
errno = 0; errno = 0;
if (--len < 0) if (!len--)
return -1; return -1;
while (len) { while(len) {
a.a = ptrace(PTRACE_PEEKTEXT, task->pid, addr, 0); a.a = ptrace(PTRACE_PEEKTEXT, task->pid, addr, 0);
if (unlikely(a.a == -1 && errno)) { if (unlikely(a.a == -1 && errno)) {
if (num_bytes && errno == EIO) if (num_bytes && errno == EIO)

View File

@ -106,17 +106,17 @@ static int set_breakpoint_mode(struct task *task, unsigned int n, int type, int
uint32_t mode; uint32_t mode;
uint32_t dr7, mask; uint32_t dr7, mask;
mask = (0b1111 << (16 + 4 * n)) | (0b11 << (2 * n)); mask = (0b1111U << (16 + 4 * n)) | (0b11U << (2 * n));
switch(type) { switch(type) {
case BP_X: case BP_X:
mode = 0b0000; mode = 0b0000U;
break; break;
case BP_W: case BP_W:
mode = 0b0001; mode = 0b0001U;
break; break;
case BP_RW: case BP_RW:
mode = 0b0011; mode = 0b0011U;
break; break;
default: default:
fprintf(stderr, "invalid hw breakpoint type\n"); 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) { switch(len) {
case 1: case 1:
mode |= 0b0000; mode |= 0b0000U;
break; break;
case 2: case 2:
mode |= 0b0100; mode |= 0b0100U;
break; break;
case 4: case 4:
mode |= 0b1100; mode |= 0b1100U;
break; break;
case 8: case 8:
mode |= 0b1000; mode |= 0b1000U;
break; break;
} }
@ -143,19 +143,19 @@ static int set_breakpoint_mode(struct task *task, unsigned int n, int type, int
dr7 |= mode << (16 + 4 * n); dr7 |= mode << (16 + 4 * n);
if (local) { if (local) {
dr7 |= 0b01 << (2 * n); dr7 |= 0b01U << (2 * n);
dr7 |= 1 << 8; dr7 |= 1 << 8;
} }
else else
if (!(dr7 & 0b01010101)) if (!(dr7 & 0b01010101U))
dr7 &= ~(1 << 8); dr7 &= ~(1 << 8);
if (global) { if (global) {
dr7 |= 0b10 << (2 * n); dr7 |= 0b10U << (2 * n);
dr7 |= 1 << 9; dr7 |= 1 << 9;
} }
else else
if (!(dr7 & 0b10101010)) if (!(dr7 & 0b10101010U))
dr7 &= ~(1 << 9); dr7 &= ~(1 << 9);
return apply_hw_bp(task, dr7); return apply_hw_bp(task, dr7);
@ -183,14 +183,14 @@ int reset_hw_bp(struct task *task, unsigned int n)
{ {
uint32_t dr7, mask; 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; dr7 = task->arch.dr7 & ~mask;
if (!(dr7 & 0b01010101)) if (!(dr7 & 0b01010101U))
dr7 &= ~(1 << 8); dr7 &= ~(1 << 8);
if (!(dr7 & 0b10101010)) if (!(dr7 & 0b10101010U))
dr7 &= ~(1 << 9); dr7 &= ~(1 << 9);
return apply_hw_bp(task, dr7); return apply_hw_bp(task, dr7);
@ -210,7 +210,7 @@ int arch_task_init(struct task *task)
{ {
unsigned int i; unsigned int i;
for(i = 0; i < HW_BREAKPOINTS; ++i) for(i = 0; i != HW_BREAKPOINTS; ++i)
task->arch.hw_bp[i] = 0; task->arch.hw_bp[i] = 0;
return _apply_hw_bp(task, 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) int arch_task_clone(struct task *retp, struct task *task)
{ {
(void)retp;
(void)task;
return 0; return 0;
} }

View File

@ -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) static inline int is_plt_entry(struct dwarf_addr_space *as)
{ {
(void)as;
#if 0 #if 0
struct dwarf_cursor *c = &as->cursor; struct dwarf_cursor *c = &as->cursor;
uint8_t data[12]; 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 int i;
unsigned char *addr = libref->image_addr + ip - p->off - libref->load_addr; 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])) if (unlikely((addr[i] & p->mask[i]) != p->op[i]))
break; break;
} }

143
task.c
View File

@ -54,15 +54,19 @@ struct pid_hash *pid_hash[PID_HASH_SIZE];
#ifndef OS_HAVE_PROCESS_DATA #ifndef OS_HAVE_PROCESS_DATA
static inline int os_task_init(struct task *task) static inline int os_task_init(struct task *task)
{ {
(void)task;
return 0; return 0;
} }
static inline void os_task_destroy(struct task *task) 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; return 0;
} }
#endif #endif
@ -70,15 +74,19 @@ static inline int os_task_clone(struct task *retp, struct task *task)
#ifndef ARCH_HAVE_PROCESS_DATA #ifndef ARCH_HAVE_PROCESS_DATA
static inline int arch_task_init(struct task *task) static inline int arch_task_init(struct task *task)
{ {
(void)task;
return 0; return 0;
} }
static inline void arch_task_destroy(struct task *task) 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; return 0;
} }
#endif #endif
@ -101,29 +109,53 @@ static inline void delete_pid(struct task *task)
static inline void insert_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 = malloc(sizeof(*entry) + 8 * sizeof(entry->tasks[0]));
entry->num = 0; entry->num = 0;
entry->size = 8; entry->size = 8;
pid_hash[PID_HASH(task->pid)] = entry; pid_hash[pidhash] = entry;
} }
else else
if (entry->size == entry->num) { if (entry->size == entry->num) {
entry->size += 8; entry->size += 8;
entry = realloc(entry, sizeof(*entry) + entry->size * sizeof(entry->tasks[0])); 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; 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 -1;
return backtrace_init(leader); return backtrace_init(leader);
@ -175,8 +207,6 @@ static int task_init(struct task *task)
list_add_tail(&task->task_list, &leader->task_list); list_add_tail(&task->task_list, &leader->task_list);
} }
task->attached = 1;
breakpoint_hw_destroy(task); breakpoint_hw_destroy(task);
return 0; return 0;
@ -216,10 +246,18 @@ static void task_destroy(struct task *task)
task->deleted = 1; task->deleted = 1;
stop_task(task);
if (task->event.type == EVENT_BREAKPOINT)
breakpoint_put(task->event.e_un.breakpoint);
arch_task_destroy(task); arch_task_destroy(task);
os_task_destroy(task); os_task_destroy(task);
detach_task(task); task_reset_bp(task);
remove_event(task);
breakpoint_hw_destroy(task);
delete_pid(task); delete_pid(task);
untrace_task(task);
if (leader != task) { if (leader != task) {
list_del(&task->task_list); list_del(&task->task_list);
@ -239,9 +277,11 @@ struct task *task_new(pid_t pid)
memset(task, 0, sizeof(*task)); memset(task, 0, sizeof(*task));
task->pid = pid; task->pid = pid;
task->traced = 0; task->attached = 0;
task->stopped = 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->task_list);
INIT_LIST_HEAD(&task->leader_list); INIT_LIST_HEAD(&task->leader_list);
@ -278,8 +318,9 @@ int process_exec(struct task *task)
{ {
struct task *leader = task->leader; struct task *leader = task->leader;
breakpoint_invalidate_all(leader);
each_task(leader, &remove_task_cb, leader); each_task(leader, &remove_task_cb, leader);
breakpoint_disable_all(leader);
os_task_destroy(leader); os_task_destroy(leader);
arch_task_destroy(leader); arch_task_destroy(leader);
@ -290,13 +331,10 @@ int process_exec(struct task *task)
if (task_init(leader) < 0) if (task_init(leader) < 0)
goto fail; goto fail;
if (server_connected())
task->attached = 0;
assert(leader->leader == leader); assert(leader->leader == leader);
assert(leader->threads_stopped == 1); assert(leader->threads_stopped == 1);
if (leader_setup(leader) < 0) if (leader_setup(leader, 0) < 0)
goto fail; goto fail;
return 0; return 0;
@ -310,8 +348,6 @@ struct task *task_create(char **argv)
struct task *task; struct task *task;
pid_t pid; pid_t pid;
debug(DEBUG_FUNCTION, "`%s'", options.command);
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
perror("fork"); perror("fork");
@ -320,6 +356,7 @@ struct task *task_create(char **argv)
if (!pid) { /* child */ if (!pid) { /* child */
change_uid(); change_uid();
trace_me(); trace_me();
execvp(options.command, argv); execvp(options.command, argv);
fprintf(stderr, "Can't execute `%s': %s\n", options.command, strerror(errno)); 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) if (trace_set_options(task) < 0)
goto fail2; goto fail2;
if (server_connected()) if (leader_setup(task, 0) < 0)
task->attached = 0; goto fail1;
if (leader_setup(task) < 0) if (handle_event(task))
goto fail1; goto fail1;
return task; return task;
@ -354,15 +391,11 @@ fail1:
int task_clone(struct task *task, struct task *newtask) int task_clone(struct task *task, struct task *newtask)
{ {
assert(newtask->attached);
assert(newtask->leader != newtask); 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); assert(newtask->backtrace == NULL);
newtask->is_64bit = task->is_64bit; newtask->is_64bit = task->is_64bit;
newtask->attached = task->attached;
breakpoint_hw_clone(newtask); breakpoint_hw_clone(newtask);
@ -374,10 +407,7 @@ int task_fork(struct task *task, struct task *newtask)
struct task *leader = task->leader; struct task *leader = task->leader;
assert(newtask->leader == newtask); assert(newtask->leader == newtask);
assert(newtask->event.type == EVENT_SIGNAL); assert(newtask->attached);
assert(newtask->event.e_un.signum == 0);
assert(newtask->traced);
assert(newtask->stopped);
assert(newtask->backtrace == NULL); assert(newtask->backtrace == NULL);
newtask->is_64bit = task->is_64bit; newtask->is_64bit = task->is_64bit;
@ -454,7 +484,9 @@ fail1:
static void show_attached(struct task *task, void *data) 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; old_ntasks = ntasks;
} }
if (leader_setup(leader) < 0) if (leader_setup(leader, 1) < 0)
goto fail1; goto fail1;
list_for_each(it, &leader->task_list) { 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; struct list_head *it, *next;
(*cb)(leader, data);
list_for_each_safe(it, next, &leader->task_list) { list_for_each_safe(it, next, &leader->task_list) {
struct task *task = container_of(it, struct task, task_list); struct task *task = container_of(it, struct task, task_list);
(*cb)(task, data); (*cb)(task, data);
}; };
(*cb)(leader, data);
} }
void remove_task(struct task *task) void remove_task(struct task *task)
@ -572,8 +603,18 @@ void remove_proc(struct task *leader)
assert(leader->leader == leader); assert(leader->leader == leader);
breakpoint_disable_all(leader); each_task(leader, &remove_task_cb, leader);
each_task(leader, &remove_task_cb, NULL); 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) 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) { for(i = 0; i < ARRAY_SIZE(pid_hash); ++i) {
struct pid_hash *entry = pid_hash[i]; struct pid_hash *entry = pid_hash[i];
unsigned int n = entry->num;
if (n) { if (entry) {
struct task **p = alloca(n * sizeof(*p)); unsigned int n = entry->num;
memcpy(p, entry->tasks, n * sizeof(*p)); if (n) {
struct task **p = alloca(n * sizeof(*p));
do { memcpy(p, entry->tasks, n * sizeof(*p));
(*cb)(*p++);
} while(--n); 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;
}

39
task.h
View File

@ -43,12 +43,12 @@ struct task {
struct event event; struct event event;
unsigned int is_64bit:1; unsigned int is_64bit:1;
unsigned int traced:1;
unsigned int attached:1; unsigned int attached:1;
unsigned int deleted:1; unsigned int deleted:1;
unsigned int about_exit:1; unsigned int about_exit:1;
unsigned int was_stopped:1;
unsigned int stopped:1; unsigned int stopped:1;
unsigned int is_new:1;
unsigned int bad:1;
struct breakpoint *breakpoint; struct breakpoint *breakpoint;
struct library_symbol *libsym; struct library_symbol *libsym;
@ -101,6 +101,12 @@ struct task {
/* halt time for debugging purpose */ /* halt time for debugging purpose */
struct timespec halt_time; 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 #if HW_BREAKPOINTS > 1
/* set in leader: list of hw breakpoints */ /* set in leader: list of hw breakpoints */
struct list_head hw_bp_list; 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. */ /* 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); 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. */ /* 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); void remove_proc(struct task *leader);
/* halt a task */
void stop_task(struct task *task);
/* return true if no more task is traced */ /* return true if no more task is traced */
int task_list_empty(void); int task_list_empty(void);
@ -166,27 +178,6 @@ struct pid_hash {
#define PID_HASH_SIZE 256 #define PID_HASH_SIZE 256
extern struct pid_hash *pid_hash[PID_HASH_SIZE]; extern struct pid_hash *pid_hash[PID_HASH_SIZE];
extern struct task *pid2task(pid_t pid);
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;
}
#endif #endif

32
trace.c
View File

@ -46,15 +46,18 @@ int skip_breakpoint(struct task *task, struct breakpoint *bp)
{ {
debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr); debug(DEBUG_PROCESS, "pid=%d, addr=%#lx", task->pid, bp->addr);
if (task->event.type != EVENT_NONE) assert(task->event.type == EVENT_BREAKPOINT);
return 1; assert(task->stopped);
assert(task->skip_bp == NULL);
if (bp->enabled && !bp->hw) { if (bp->enabled && !bp->hw) {
int ret = 0; int ret = 0;
struct timespec start; struct timespec start;
if (task->skip_bp) if (task->skip_bp) {
task->event.type = EVENT_NONE;
return 1; return 1;
}
if (unlikely(options.verbose > 1)) if (unlikely(options.verbose > 1))
start_time(&start); start_time(&start);
@ -68,6 +71,7 @@ int skip_breakpoint(struct task *task, struct breakpoint *bp)
if (unlikely(ret)) { if (unlikely(ret)) {
task->skip_bp = breakpoint_get(bp); task->skip_bp = breakpoint_get(bp);
assert(task->skip_bp);
return ret; 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) static void detach_cb(struct task *task, void *data)
{ {
(void)data;
remove_task(task); remove_task(task);
} }
@ -115,7 +103,7 @@ void detach_proc(struct task *leader)
breakpoint_disable_all(leader); breakpoint_disable_all(leader);
if (unlikely(options.verbose > 1)) 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); each_task(leader, &detach_cb, NULL);
} }

View File

@ -26,7 +26,6 @@
#include "forward.h" #include "forward.h"
void fix_about_exit(struct task *task); void fix_about_exit(struct task *task);
void detach_task(struct task *task);
void detach_proc(struct task *leader); void detach_proc(struct task *leader);
int skip_breakpoint(struct task *task, struct breakpoint *bp); int skip_breakpoint(struct task *task, struct breakpoint *bp);