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