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