diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/probe_lock.h | 68 | ||||
-rw-r--r-- | runtime/staprun/mainloop.c | 26 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 2 | ||||
-rw-r--r-- | runtime/stat-common.c | 2 | ||||
-rw-r--r-- | runtime/string.c | 6 | ||||
-rw-r--r-- | runtime/task_finder.c | 4 | ||||
-rw-r--r-- | runtime/transport/transport.c | 38 | ||||
-rw-r--r-- | runtime/transport/transport.h | 3 | ||||
-rw-r--r-- | runtime/unwind.c | 43 | ||||
-rw-r--r-- | runtime/unwind/unwind.h | 6 |
10 files changed, 155 insertions, 43 deletions
diff --git a/runtime/probe_lock.h b/runtime/probe_lock.h new file mode 100644 index 00000000..1915d4ff --- /dev/null +++ b/runtime/probe_lock.h @@ -0,0 +1,68 @@ +/* probe locking header file + * Copyright (C) 2009 Red Hat Inc. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#ifndef _PROBE_LOCK_H +#define _PROBE_LOCK_H + +#include <linux/spinlock.h> + +// XXX: old 2.6 kernel hack +#ifndef read_trylock +#define read_trylock(x) ({ read_lock(x); 1; }) +#endif + +struct stp_probe_lock { + #ifdef STP_TIMING + atomic_t *skipped; + #endif + rwlock_t *lock; + unsigned write_p; +}; + + +static void +stp_unlock_probe(const struct stp_probe_lock *locks, unsigned num_locks) +{ + unsigned i; + for (i = num_locks; i-- > 0;) { + if (locks[i].write_p) + write_unlock(locks[i].lock); + else + read_unlock(locks[i].lock); + } +} + + +static unsigned +stp_lock_probe(const struct stp_probe_lock *locks, unsigned num_locks) +{ + unsigned i, numtrylock = 0; + for (i = 0; i < num_locks; ++i) { + if (locks[i].write_p) + while (!write_trylock(locks[i].lock) && + (++numtrylock < MAXTRYLOCK)) + ndelay (TRYLOCKDELAY); + else + while (!read_trylock(locks[i].lock) && + (++numtrylock < MAXTRYLOCK)) + ndelay (TRYLOCKDELAY); + if (unlikely(numtrylock >= MAXTRYLOCK)) { + atomic_inc(&skipped_count); + #ifdef STP_TIMING + atomic_inc(locks[i].skipped); + #endif + stp_unlock_probe(locks, i); + return 0; + } + } + return 1; +} + + +#endif /* _PROBE_LOCK_H */ diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index cf8bef9a..ab228937 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -38,7 +38,7 @@ static void *signal_thread(void *arg) } dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum)); if (signum == SIGQUIT) - cleanup_and_exit(1); + cleanup_and_exit(1, 0); else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) { // send STP_EXIT rc = write(control_channel, &btype, sizeof(btype)); @@ -383,7 +383,7 @@ int init_stapio(void) /* cleanup_and_exit() closed channels, frees memory, * removes the module (if necessary) and exits. */ -void cleanup_and_exit(int detach) +void cleanup_and_exit(int detach, int rc) { static int exiting = 0; const char *staprun; @@ -467,7 +467,7 @@ void cleanup_and_exit(int detach) } if (WIFEXITED(rstatus)) { - _exit(WEXITSTATUS(rstatus)); /* only possibility for rc=0 exit */ + _exit(rc ?: WEXITSTATUS(rstatus)); } _exit(-1); } @@ -484,8 +484,9 @@ int stp_main_loop(void) uint32_t type; FILE *ofp = stdout; char recvbuf[8196]; + int error_detected = 0; - setvbuf(ofp, (char *)NULL, _IOLBF, 0); + setvbuf(ofp, (char *)NULL, _IONBF, 0); setup_main_signals(); dbug(2, "in main loop\n"); @@ -511,18 +512,21 @@ int stp_main_loop(void) case STP_REALTIME_DATA: if (write_realtime_data(data, nb)) { _perr("write error (nb=%ld)", (long)nb); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } break; #endif case STP_OOB_DATA: eprintf("%s", (char *)data); + if (strncmp(data, "ERROR:", 5) == 0){ + error_detected = 1; + } break; case STP_EXIT: { /* module asks us to unload it and exit */ dbug(2, "got STP_EXIT\n"); - cleanup_and_exit(0); + cleanup_and_exit(0, error_detected); break; } case STP_REQUEST_EXIT: @@ -540,7 +544,7 @@ int stp_main_loop(void) if (t->res < 0) { if (target_cmd) kill(target_pid, SIGKILL); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } else if (target_cmd) { dbug(1, "detaching pid %d\n", target_pid); #if WORKAROUND_BZ467568 @@ -555,7 +559,7 @@ int stp_main_loop(void) perror ("ptrace detach"); if (target_cmd) kill(target_pid, SIGKILL); - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } #endif } @@ -573,15 +577,15 @@ int stp_main_loop(void) struct _stp_msg_start ts; if (use_old_transport) { if (init_oldrelayfs() < 0) - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } else { if (init_relayfs() < 0) - cleanup_and_exit(0); + cleanup_and_exit(0, 1); } ts.target = target_pid; send_request(STP_START, &ts, sizeof(ts)); if (load_only) - cleanup_and_exit(1); + cleanup_and_exit(1, 0); break; } default: diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index f9f01003..9cdbc861 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -114,7 +114,7 @@ int init_staprun(void); int init_stapio(void); int stp_main_loop(void); int send_request(int type, void *data, int len); -void cleanup_and_exit (int); +void cleanup_and_exit (int, int); int init_ctl_channel(const char *name, int verb); void close_ctl_channel(void); int init_relayfs(void); diff --git a/runtime/stat-common.c b/runtime/stat-common.c index e6fd3a11..7123dc8e 100644 --- a/runtime/stat-common.c +++ b/runtime/stat-common.c @@ -270,7 +270,7 @@ static void _stp_stat_print_histogram_buf(char *buf, size_t size, Hist st, stat for (j = 0; j < v; ++j) HIST_PRINTF("@"); - HIST_PRINTF("%*lld\n", HIST_WIDTH - v + 1 + cnt_space, sd->histogram[i]); + HIST_PRINTF("%*lld\n", (int)(HIST_WIDTH - v + 1 + cnt_space), sd->histogram[i]); } HIST_PRINTF("\n"); #undef HIST_PRINTF diff --git a/runtime/string.c b/runtime/string.c index cdafbf64..b3f81f0e 100644 --- a/runtime/string.c +++ b/runtime/string.c @@ -21,7 +21,11 @@ */ /** Sprintf into a string. - * Like printf, except output goes into a string. + * Like printf, except output goes into a string. + * + * NB: these are script language printf formatting directives, where + * %d ints are 64-bits etc, so we can't use gcc level attribute printf + * to type-check the arguments. * * @param str string * @param fmt A printf-style format string followed by a diff --git a/runtime/task_finder.c b/runtime/task_finder.c index 6b50f1b9..e89ac8ee 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -87,15 +87,15 @@ struct stap_task_finder_target { struct list_head callback_list_head; struct list_head callback_list; struct utrace_engine_ops ops; + size_t pathlen; unsigned engine_attached:1; unsigned mmap_events:1; unsigned munmap_events:1; unsigned mprotect_events:1; - size_t pathlen; /* public: */ - const char *procname; pid_t pid; + const char *procname; stap_task_finder_callback callback; stap_task_finder_mmap_callback mmap_callback; stap_task_finder_munmap_callback munmap_callback; diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index f5efce9f..bab5efa9 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -19,6 +19,7 @@ #include <linux/namei.h> #include <linux/workqueue.h> #include <linux/delay.h> +#include <linux/mutex.h> static int _stp_exit_flag = 0; @@ -30,6 +31,9 @@ static int _stp_ctl_attached = 0; static pid_t _stp_target = 0; static int _stp_probes_started = 0; +static int _stp_exit_called = 0; +static DEFINE_MUTEX(_stp_transport_mutex); + // For now, disable transport version 3 (unless STP_USE_RING_BUFFER is // defined). @@ -81,22 +85,26 @@ static struct workqueue_struct *_stp_wq; static void _stp_handle_start(struct _stp_msg_start *st) { - dbug_trans(1, "stp_handle_start\n"); + mutex_lock(&_stp_transport_mutex); + if (!_stp_exit_called) { + dbug_trans(1, "stp_handle_start\n"); #ifdef STAPCONF_VM_AREA - { /* PR9740: workaround for kernel valloc bug. */ - void *dummy; - dummy = alloc_vm_area (PAGE_SIZE); - free_vm_area (dummy); - } + { /* PR9740: workaround for kernel valloc bug. */ + void *dummy; + dummy = alloc_vm_area (PAGE_SIZE); + free_vm_area (dummy); + } #endif - _stp_target = st->target; - st->res = probe_start(); - if (st->res >= 0) - _stp_probes_started = 1; + _stp_target = st->target; + st->res = probe_start(); + if (st->res >= 0) + _stp_probes_started = 1; - _stp_ctl_send(STP_START, st, sizeof(*st)); + _stp_ctl_send(STP_START, st, sizeof(*st)); + } + mutex_unlock(&_stp_transport_mutex); } /* common cleanup code. */ @@ -106,8 +114,7 @@ static void _stp_handle_start(struct _stp_msg_start *st) /* when someone does /sbin/rmmod on a loaded systemtap module. */ static void _stp_cleanup_and_exit(int send_exit) { - static int _stp_exit_called = 0; - + mutex_lock(&_stp_transport_mutex); if (!_stp_exit_called) { int failures; @@ -135,6 +142,7 @@ static void _stp_cleanup_and_exit(int send_exit) _stp_ctl_send(STP_EXIT, NULL, 0); dbug_trans(1, "done with ctl_send STP_EXIT\n"); } + mutex_unlock(&_stp_transport_mutex); } static void _stp_request_exit(void) @@ -293,7 +301,7 @@ err0: static inline void _stp_lock_inode(struct inode *inode) { -#ifdef DEFINE_MUTEX +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) mutex_lock(&inode->i_mutex); #else down(&inode->i_sem); @@ -302,7 +310,7 @@ static inline void _stp_lock_inode(struct inode *inode) static inline void _stp_unlock_inode(struct inode *inode) { -#ifdef DEFINE_MUTEX +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) mutex_unlock(&inode->i_mutex); #else up(&inode->i_sem); diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index 871e37b3..ddd85495 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -13,9 +13,6 @@ static int _stp_ctl_write(int type, void *data, unsigned len); -static int _stp_transport_init(void); -static void _stp_transport_close(void); - /* STP_CTL_BUFFER_SIZE is the maximum size of a message */ /* exchanged on the control channel. */ #if STP_TRANSPORT_VERSION == 1 diff --git a/runtime/unwind.c b/runtime/unwind.c index 00108a39..7607770e 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -88,7 +88,7 @@ static sleb128_t get_sleb128(const u8 **pcur, const u8 *end) /* given an FDE, find its CIE */ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, - int is_ehframe) + uint32_t table_len, int is_ehframe) { const u32 *cie; @@ -118,6 +118,11 @@ static const u32 *cie_for_fde(const u32 *fde, void *unwind_data, else cie = unwind_data + fde[1]; + /* Make sure address falls in the table */ + if (((void *)cie) < ((void*)unwind_data) + || ((void*)cie) > ((void*)(unwind_data + table_len))) + return NULL; + if (*cie <= sizeof(*cie) + 4 || *cie >= fde[1] - sizeof(*fde) || (*cie & (sizeof(*cie) - 1)) || (cie[1] != 0xffffffff && cie[1] != 0)) { @@ -200,7 +205,8 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrTy return value; } -static signed fde_pointer_type(const u32 *cie) +static signed fde_pointer_type(const u32 *cie, void *unwind_data, + uint32_t table_len) { const u8 *ptr = (const u8 *)(cie + 2); unsigned version = *ptr; @@ -212,11 +218,16 @@ static signed fde_pointer_type(const u32 *cie) const u8 *end = (const u8 *)(cie + 1) + *cie; uleb128_t len; + /* end of cie should fall within unwind table. */ + if (((void*)end) < ((void *)unwind_data) + || ((void *)end) > ((void *)(unwind_data + table_len))) + return -1; + /* check if augmentation size is first (and thus present) */ if (*ptr != 'z') return -1; /* check if augmentation string is nul-terminated */ - if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) + if ((ptr = memchr(aug = (const void *)ptr, 0, end - ptr)) == NULL) return -1; ++ptr; /* skip terminator */ get_uleb128(&ptr, end); /* skip code alignment */ @@ -267,6 +278,10 @@ static void set_rule(uleb128_t reg, enum item_location where, uleb128_t value, s } } +/* Limit the number of instructions we process. Arbitrary limit. + 512 should be enough for anybody... */ +#define MAX_CFI 512 + static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, signed ptrType, struct unwind_state *state) { union { @@ -276,6 +291,9 @@ static int processCFI(const u8 *start, const u8 *end, unsigned long targetLoc, s } ptr; int result = 1; + if (end - start > MAX_CFI) + return 0; + dbug_unwind(1, "targetLoc=%lx state->loc=%lx\n", targetLoc, state->loc); if (start != state->cieStart) { state->loc = state->org; @@ -606,10 +624,10 @@ static int unwind_frame(struct unwind_frame_info *frame, /* found the fde, now set startLoc and endLoc */ if (fde != NULL) { - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (likely(cie != NULL && cie != &bad_cie && cie != ¬_fde)) { ptr = (const u8 *)(fde + 2); - ptrType = fde_pointer_type(cie); + ptrType = fde_pointer_type(cie, table, table_len); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); startLoc = adjustStartLoc(startLoc, m, s, ptrType, is_ehframe); @@ -632,12 +650,12 @@ static int unwind_frame(struct unwind_frame_info *frame, for (fde = table, tableSize = table_len; cie = NULL, tableSize > sizeof(*fde) && tableSize - sizeof(*fde) >= *fde; tableSize -= sizeof(*fde) + *fde, fde += 1 + *fde / sizeof(*fde)) { dbug_unwind(3, "fde=%lx tableSize=%d\n", (long)*fde, (int)tableSize); - cie = cie_for_fde(fde, table, is_ehframe); + cie = cie_for_fde(fde, table, table_len, is_ehframe); if (cie == &bad_cie) { cie = NULL; break; } - if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie)) < 0) + if (cie == NULL || cie == ¬_fde || (ptrType = fde_pointer_type(cie, table, table_len)) < 0) continue; ptr = (const u8 *)(fde + 2); @@ -666,6 +684,12 @@ static int unwind_frame(struct unwind_frame_info *frame, state.cieEnd = ptr; /* keep here temporarily */ ptr = (const u8 *)(cie + 2); end = (const u8 *)(cie + 1) + *cie; + + /* end should fall within unwind table. */ + if (((void *)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + frame->call_frame = 1; if ((state.version = *ptr) != 1) { dbug_unwind(1, "CIE version number is %d. 1 is supported.\n", state.version); @@ -723,6 +747,11 @@ static int unwind_frame(struct unwind_frame_info *frame, state.cieEnd = end; end = (const u8 *)(fde + 1) + *fde; + /* end should fall within unwind table. */ + if (((void*)end) < table + || ((void *)end) > ((void *)(table + table_len))) + goto err; + /* skip augmentation */ if (((const char *)(cie + 2))[1] == 'z') { uleb128_t augSize = get_uleb128(&ptr, end); diff --git a/runtime/unwind/unwind.h b/runtime/unwind/unwind.h index 285a3a34..023ea603 100644 --- a/runtime/unwind/unwind.h +++ b/runtime/unwind/unwind.h @@ -143,8 +143,10 @@ static unsigned long read_pointer(const u8 **pLoc, const void *end, signed ptrType); static const u32 bad_cie, not_fde; -static const u32 *cie_for_fde(const u32 *fde, void *table, int is_ehframe); -static signed fde_pointer_type(const u32 *cie); +static const u32 *cie_for_fde(const u32 *fde, void *table, + uint32_t table_len, int is_ehframe); +static signed fde_pointer_type(const u32 *cie, + void *table, uint32_t table_len); #endif /* STP_USE_DWARF_UNWINDER */ |