diff options
author | Dave Brolley <brolley@redhat.com> | 2009-06-22 11:37:08 -0400 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-06-22 11:37:08 -0400 |
commit | d6454850dd3c9d74b700924b56378c4c9ab9fa57 (patch) | |
tree | 6665a24339ca689b1156ea7e93d4f93811c3f794 | |
parent | d729143af5242b17645d3f405141918940680894 (diff) | |
parent | 4116c576d5654287b0af598aee4a14eb2af73224 (diff) | |
download | systemtap-steved-d6454850dd3c9d74b700924b56378c4c9ab9fa57.tar.gz systemtap-steved-d6454850dd3c9d74b700924b56378c4c9ab9fa57.tar.xz systemtap-steved-d6454850dd3c9d74b700924b56378c4c9ab9fa57.zip |
Merge branch 'master' of git://sources.redhat.com/git/systemtap
52 files changed, 1765 insertions, 922 deletions
@@ -2341,11 +2341,13 @@ dwflpp::get_blacklist_section(Dwarf_Addr addr) Dwarf_Addr -dwflpp::relocate_address(Dwarf_Addr addr, +dwflpp::relocate_address(Dwarf_Addr dw_addr, string& reloc_section, string& blacklist_section) { - Dwarf_Addr reloc_addr = addr; + // PR10273 + // libdw address, so adjust for bias gotten from dwfl_module_getdwarf + Dwarf_Addr reloc_addr = dw_addr + module_bias; if (!module) { assert(module_name == TOK_KERNEL); @@ -2364,14 +2366,13 @@ dwflpp::relocate_address(Dwarf_Addr addr, if (reloc_section == "" && dwfl_module_relocations (module) == 1) { - blacklist_section = get_blacklist_section(addr); + blacklist_section = get_blacklist_section(dw_addr); reloc_section = ".dynamic"; - reloc_addr += module_bias; // PR10273 } } else { - blacklist_section = get_blacklist_section(addr); + blacklist_section = get_blacklist_section(dw_addr); reloc_section = ".absolute"; } return reloc_addr; diff --git a/includes/sys/sdt.h b/includes/sys/sdt.h index 0f86cc3b..07ece7c1 100644 --- a/includes/sys/sdt.h +++ b/includes/sys/sdt.h @@ -32,6 +32,7 @@ STAP_PROBE_ADDR "1b\n" \ "\t.align 8\n" \ STAP_PROBE_ADDR #arg "\n" \ + "\t.int 0\n" \ "\t.previous\n") #define STAP_PROBE_DATA(probe, guard, arg) \ diff --git a/initscript/systemtap.in b/initscript/systemtap.in index 918455d6..7552df44 100644 --- a/initscript/systemtap.in +++ b/initscript/systemtap.in @@ -74,7 +74,7 @@ log () { # message echo `LC_ALL=en date +"%b %e %T"`": $1" >> "$LOG_FILE" } clog () { # message [-n] - echo $2 $1 + echo $2 "$1" log "$1" } slog () { # message @@ -154,7 +154,7 @@ parse_args () { # arguments CMD=$1 shift 1 -OPTS=`getopt -s bash -u -o 'r:c:R' -- $@` +OPTS=`getopt -s bash -u -o 'r:c:Ry' -- $@` if [ $? -ne 0 ]; then slog "Error: Argument parse error: $@" failure $"parse error" @@ -381,7 +381,7 @@ compile_script () { # script checkcache clog " Compiling $1 ... " -n tmpdir=`mktemp -d -p "$TEMP_PATH" cache.XXXXXXXX` if [ $? -ne 0 ]; then - clog "failed to create temporary directory." + clog "Failed to create temporary directory." return 1 fi pushd "$tmpdir" &> /dev/null @@ -460,7 +460,7 @@ start_script () { # script clog " Starting $1 ... " -n tmpdir=`mktemp -d -p "$TEMP_PATH" cache.XXXXXXXX` # bz7097 if [ $? -ne 0 ]; then - clog "failed to create temporary directory." + clog "Failed to create temporary directory." return 1 fi pushd "$tmpdir" &> /dev/null @@ -486,7 +486,7 @@ start () { fi if [ -z "$SCRIPTS" ]; then - do_warning $"no scripts exist." + do_warning $"No scripts exist." return 5 # program is not installed fi @@ -553,7 +553,7 @@ stop () { done for s in $sl; do stop_script $s - [ $? -ne 0 ] && might_fail $"Failed to run \"$s\". " + [ $? -ne 0 ] && might_fail $"Failed to stop \"$s\". " done might_success $"$prog stopping " return 0 @@ -563,6 +563,10 @@ status () { local s pid ret r [ -z "$SCRIPTS" ] && SCRIPTS=`get_all_runnings` ret=3 + if [ -z "$SCRIPTS" ] ; then + echo "No systemtap scripts are present" + return $ret + fi for s in $SCRIPTS; do check_running $s r=$? @@ -581,7 +585,7 @@ status () { compile () { local s ss - clog $"Compiling $prog: " -n + clog $"Compiling systemtap scripts: " -n prepare_cache_dir if [ $? -ne 0 ]; then do_failure $"Failed to make cache directory ($CACHE_PATH)" @@ -607,7 +611,7 @@ compile () { # Cleanup caches cleanup () { local s ss ret - clog $"Cleaning up $prog: " -n + clog $"Cleaning up systemtap scripts: " -n if [ ! -d "$CACHE_PATH" ]; then do_success "no cache" return 0 @@ -630,6 +634,15 @@ cleanup () { fi } +# Restart scripts +function restart () { + [ -z "$OPT_SCRIPTS" ] && SCRIPTS=`get_all_runnings` + [ "$SCRIPTS" ] && stop + echo + start + return $? +} + RETVAL=0 case $CMD in @@ -642,13 +655,12 @@ case $CMD in RETVAL=$? ;; restart|force-reload) - stop - start + restart RETVAL=$? ;; status) status - RETVAL=$? + exit $? ;; compile) compile diff --git a/runtime/debug.h b/runtime/debug.h index e8b2e701..5c9cca74 100644 --- a/runtime/debug.h +++ b/runtime/debug.h @@ -14,7 +14,6 @@ * _dbug() writes to systemtap stderr. * errk() writes to the system log. */ -static int _stp_transport_state = 0; #define _dbug(args...) _stp_dbug(__FUNCTION__, __LINE__, args) @@ -39,8 +38,14 @@ static int _stp_transport_state = 0; printk(args); \ } \ } while (0) + +#define dbug_trans2(args...) do { \ + printk("%s:%d ",__FUNCTION__, __LINE__); \ + printk(args); \ + } while (0) #else #define dbug_trans(level, args...) ; +#define dbug_trans2(args...) ; #endif #ifdef DEBUG_UNWIND /* stack unwinder */ diff --git a/runtime/print.c b/runtime/print.c index 964a73c2..69e04073 100644 --- a/runtime/print.c +++ b/runtime/print.c @@ -13,6 +13,8 @@ #include "string.h" +#include "vsprintf.c" +#include "print.h" #include "transport/transport.c" #include "vsprintf.c" @@ -86,14 +88,8 @@ static void _stp_print_cleanup (void) #endif #if !defined(RELAY_GUEST) -/* The relayfs API changed between 2.6.15 and 2.6.16. */ -/* Use the appropriate print flush function. */ -#ifdef STP_OLD_TRANSPORT -#include "print_old.c" -#else -#include "print_new.c" -#endif +#include "print_flush.c" #if defined(RELAY_HOST) EXPORT_SYMBOL_GPL(EXPORT_FN(stp_print_flush)); #endif diff --git a/runtime/print.h b/runtime/print.h new file mode 100644 index 00000000..c2731a2f --- /dev/null +++ b/runtime/print.h @@ -0,0 +1,17 @@ +/* -*- linux-c -*- + * 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 _STP_PRINT_H_ +#define _STP_PRINT_H_ + +static int _stp_print_init(void); +static void _stp_print_cleanup(void); +static void *_stp_reserve_bytes(int numbytes); + +#endif /* _STP_PRINT_H_ */ diff --git a/runtime/print_flush.c b/runtime/print_flush.c new file mode 100644 index 00000000..52017426 --- /dev/null +++ b/runtime/print_flush.c @@ -0,0 +1,129 @@ +/* -*- linux-c -*- + * Print Flush Function + * Copyright (C) 2007-2008 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. + */ + +/** Send the print buffer to the transport now. + * Output accumulates in the print buffer until it + * is filled, or this is called. This MUST be called before returning + * from a probe or accumulated output in the print buffer will be lost. + * + * @note Preemption must be disabled to use this. + */ + +static DEFINE_SPINLOCK(_stp_print_lock); + +void EXPORT_FN(stp_print_flush)(_stp_pbuf *pb) +{ + size_t len = pb->len; + void *entry = NULL; + + /* check to see if there is anything in the buffer */ + dbug_trans(1, "len = %zu\n", len); + if (likely(len == 0)) + return; + + pb->len = 0; + + if (unlikely(_stp_transport_get_state() != STP_TRANSPORT_RUNNING)) + return; + +#ifdef STP_BULKMODE +#ifdef NO_PERCPU_HEADERS + { + char *bufp = pb->buf; + + while (len > 0) { + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } + } + +#else /* !NO_PERCPU_HEADERS */ + + { + char *bufp = pb->buf; + struct _stp_trace t = { .sequence = _stp_seq_inc(), + .pdu_len = len}; + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(sizeof(struct _stp_trace), &entry); + if (likely(entry && bytes_reserved > 0)) { + /* prevent unaligned access by using memcpy() */ + memcpy(_stp_data_entry_data(entry), &t, sizeof(t)); + _stp_data_write_commit(entry); + } + else { + atomic_inc(&_stp_transport_failures); + return; + } + + while (len > 0) { + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } + } +#endif /* !NO_PERCPU_HEADERS */ + +#else /* !STP_BULKMODE */ + +#if STP_TRANSPORT_VERSION == 1 + + if (unlikely(_stp_ctl_write(STP_REALTIME_DATA, pb->buf, len) <= 0)) + atomic_inc (&_stp_transport_failures); + +#else /* STP_TRANSPORT_VERSION != 1 */ + { + unsigned long flags; + char *bufp = pb->buf; + + dbug_trans(1, "calling _stp_data_write...\n"); + spin_lock_irqsave(&_stp_print_lock, flags); + while (len > 0) { + size_t bytes_reserved; + + bytes_reserved = _stp_data_write_reserve(len, &entry); + if (likely(entry && bytes_reserved > 0)) { + memcpy(_stp_data_entry_data(entry), bufp, + bytes_reserved); + _stp_data_write_commit(entry); + bufp += bytes_reserved; + len -= bytes_reserved; + } + else { + atomic_inc(&_stp_transport_failures); + break; + } + } + spin_unlock_irqrestore(&_stp_print_lock, flags); + } +#endif /* STP_TRANSPORT_VERSION != 1 */ +#endif /* !STP_BULKMODE */ +} diff --git a/runtime/print_new.c b/runtime/print_new.c deleted file mode 100644 index fa7b4727..00000000 --- a/runtime/print_new.c +++ /dev/null @@ -1,67 +0,0 @@ -/* -*- linux-c -*- - * Print Flush Function - * Copyright (C) 2007-2008 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. - */ - -/** Send the print buffer to the transport now. - * Output accumulates in the print buffer until it - * is filled, or this is called. This MUST be called before returning - * from a probe or accumulated output in the print buffer will be lost. - * - * @note Preemption must be disabled to use this. - */ - -static DEFINE_SPINLOCK(_stp_print_lock); - -void EXPORT_FN(stp_print_flush) (_stp_pbuf *pb) -{ - uint32_t len = pb->len; - - /* check to see if there is anything in the buffer */ - if (likely (len == 0)) - return; - - pb->len = 0; - - if (unlikely(!_stp_utt || _stp_utt->trace_state != Utt_trace_running)) - return; - -#ifdef STP_BULKMODE - { -#ifdef NO_PERCPU_HEADERS - void *buf = utt_reserve(_stp_utt, len); - if (likely(buf)) - memcpy(buf, pb->buf, len); - else - atomic_inc (&_stp_transport_failures); -#else - void *buf = utt_reserve(_stp_utt, - sizeof(struct _stp_trace) + len); - if (likely(buf)) { - struct _stp_trace t = { .sequence = _stp_seq_inc(), - .pdu_len = len}; - memcpy(buf, &t, sizeof(t)); // prevent unaligned access - memcpy(buf + sizeof(t), pb->buf, len); - } else - atomic_inc (&_stp_transport_failures); -#endif - } -#else - { - void *buf; - unsigned long flags; - spin_lock_irqsave(&_stp_print_lock, flags); - buf = utt_reserve(_stp_utt, len); - if (likely(buf)) - memcpy(buf, pb->buf, len); - else - atomic_inc (&_stp_transport_failures); - spin_unlock_irqrestore(&_stp_print_lock, flags); - } -#endif /* STP_BULKMODE */ -} diff --git a/runtime/print_old.c b/runtime/print_old.c deleted file mode 100644 index 5c117e5f..00000000 --- a/runtime/print_old.c +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- linux-c -*- - * Print Flush Function - * Copyright (C) 2007 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. - */ - -/** Send the print buffer to the transport now. - * Output accumulates in the print buffer until it - * is filled, or this is called. This MUST be called before returning - * from a probe or accumulated output in the print buffer will be lost. - * - * @note Preemption must be disabled to use this. - */ - -void EXPORT_FN(stp_print_flush) (_stp_pbuf *pb) -{ - uint32_t len = pb->len; - - /* check to see if there is anything in the buffer */ - if (likely (len == 0)) - return; - - pb->len = 0; - -#ifdef STP_BULKMODE - { -#ifdef NO_PERCPU_HEADERS - void *buf = relay_reserve(_stp_utt->rchan, len); - if (likely(buf)) - memcpy(buf, pb->buf, len); - else - atomic_inc (&_stp_transport_failures); -#else - void *buf = relay_reserve(_stp_utt->rchan, - sizeof(struct _stp_trace) + len); - if (likely(buf)) { - struct _stp_trace t = { .sequence = _stp_seq_inc(), - .pdu_len = len}; - memcpy(buf, &t, sizeof(t)); // prevent unaligned access - memcpy(buf + sizeof(t), pb->buf, len); - } else - atomic_inc (&_stp_transport_failures); -#endif - } -#else - { - if (unlikely(_stp_ctl_write(STP_REALTIME_DATA, pb->buf, len) <= 0)) - atomic_inc (&_stp_transport_failures); - } -#endif /* STP_BULKMODE */ -} diff --git a/runtime/runtime.h b/runtime/runtime.h index 9881acb7..c3bf501d 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -34,9 +34,14 @@ #if !defined (CONFIG_DEBUG_FS) && !defined (CONFIG_DEBUG_FS_MODULE) #error "DebugFS is required and was not found in the kernel." #endif +#ifdef CONFIG_RING_BUFFER +#define STP_TRANSPORT_VERSION 3 +#else +#define STP_TRANSPORT_VERSION 2 +#endif #else /* older kernels have no debugfs and older version of relayfs. */ -#define STP_OLD_TRANSPORT +#define STP_TRANSPORT_VERSION 1 #endif #ifndef stp_for_each_cpu @@ -45,6 +50,9 @@ static void _stp_dbug (const char *func, int line, const char *fmt, ...); static void _stp_error (const char *fmt, ...); +static void _stp_warn (const char *fmt, ...); + +static void _stp_exit(void); #include "debug.h" diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c index 644dfa53..010cab12 100644 --- a/runtime/staprun/common.c +++ b/runtime/staprun/common.c @@ -67,6 +67,33 @@ int stap_strfloctime(char *buf, size_t max, const char *fmt, time_t t) return (int)ret; } +int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t, int bulk) +{ + int len; + if (PATH_MAX < max) + max = PATH_MAX; + len = stap_strfloctime(buf, max, outfile_name, t); + if (len < 0) { + err("Invalid FILE name format\n"); + return -1; + } + /* special case: for testing we sometimes want to write to /dev/null */ + if (strcmp(outfile_name, "/dev/null") == 0) { + strcpy(buf, "/dev/null"); + } else { + if (bulk) { + if (snprintf_chk(&buf[len], max - len, "_cpu%d.%d", + cpu, fnum)) + return -1; + } else { + /* stream mode */ + if (snprintf_chk(&buf[len], max - len, ".%d", fnum)) + return -1; + } + } + return 0; +} + void parse_args(int argc, char **argv) { int c; diff --git a/runtime/staprun/mainloop.c b/runtime/staprun/mainloop.c index 205fdf37..7125a7bb 100644 --- a/runtime/staprun/mainloop.c +++ b/runtime/staprun/mainloop.c @@ -487,7 +487,7 @@ int stp_main_loop(void) nb -= sizeof(uint32_t); switch (type) { -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 case STP_REALTIME_DATA: if (write_realtime_data(data, nb)) { _perr("write error (nb=%ld)", (long)nb); diff --git a/runtime/staprun/relay.c b/runtime/staprun/relay.c index b9796241..f4aa139f 100644 --- a/runtime/staprun/relay.c +++ b/runtime/staprun/relay.c @@ -73,31 +73,6 @@ time_t read_backlog(int cpu, int fnum) return time_backlog[cpu][fnum & BACKLOG_MASK]; } -int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t) -{ - int len; - len = stap_strfloctime(buf, max, outfile_name, t); - if (len < 0) { - err("Invalid FILE name format\n"); - return -1; - } - if (bulkmode) { - /* special case: for testing we sometimes want to write to /dev/null */ - if (strcmp(outfile_name, "/dev/null") == 0) { - strcpy(buf, "/dev/null"); - } else { - if (snprintf_chk(&buf[len], PATH_MAX - len, - "_cpu%d.%d", cpu, fnum)) - return -1; - } - } else { - /* stream mode */ - if (snprintf_chk(&buf[len], PATH_MAX - len, ".%d", fnum)) - return -1; - } - return 0; -} - static int open_outfile(int fnum, int cpu, int remove_file) { char buf[PATH_MAX]; @@ -112,14 +87,15 @@ static int open_outfile(int fnum, int cpu, int remove_file) if (remove_file) { /* remove oldest file */ if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, - cpu, read_backlog(cpu, fnum - fnum_max)) < 0) + cpu, read_backlog(cpu, fnum - fnum_max), + bulkmode) < 0) return -1; remove(buf); /* don't care */ } write_backlog(cpu, fnum, t); } - if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0) + if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t, bulkmode) < 0) return -1; out_fd[cpu] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666); if (out_fd[cpu] < 0) { diff --git a/runtime/staprun/relay_old.c b/runtime/staprun/relay_old.c index 33d2daf3..71d8acee 100644 --- a/runtime/staprun/relay_old.c +++ b/runtime/staprun/relay_old.c @@ -87,30 +87,33 @@ static int open_oldoutfile(int fnum, int cpu, int remove_file) if (fnum_max) { if (remove_file) { /* remove oldest file */ - if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, - cpu, read_backlog(cpu, fnum - fnum_max)) < 0) + if (make_outfile_name(buf, PATH_MAX, + fnum - fnum_max, cpu, + read_backlog(cpu, fnum - fnum_max), + bulkmode) < 0) return -1; remove(buf); /* don't care */ } write_backlog(cpu, fnum, t); } - if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0) + if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t, bulkmode) < 0) return -1; } else if (bulkmode) { if (sprintf_chk(buf, "stpd_cpu%d.%d", cpu, fnum)) return -1; } else { /* stream mode */ - out_fd[cpu] = STDOUT_FILENO; + percpu_tmpfile[cpu] = stdout; return 0; } - out_fd[cpu] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666); - if (out_fd[cpu] < 0) { + if((percpu_tmpfile[cpu] = fopen(buf, "w+")) == NULL) { perr("Couldn't open output file %s", buf); return -1; } - if (set_clexec(out_fd[cpu]) < 0) + if (set_clexec(fileno(percpu_tmpfile[cpu])) < 0) { + perr("Couldn't clear exec bit of open output file %s", buf); return -1; + } return 0; } /** diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 5159f1bd..6af04042 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -94,8 +94,9 @@ extern char *__name__; /* Grabbed from linux/module.h kernel include. */ #define MODULE_NAME_LEN (64 - sizeof(unsigned long)) -/* we define this so we are compatible with old transport, but we don't have to use it. */ -#define STP_OLD_TRANSPORT +/* We define this so we are compatible with old transport, but we + * don't have to use it. */ +#define STP_TRANSPORT_VERSION 1 #include "../transport/transport_msgs.h" #define RELAYFS_MAGIC 0xF0B4A981 @@ -119,7 +120,8 @@ int init_oldrelayfs(void); void close_oldrelayfs(int); int write_realtime_data(void *data, ssize_t nb); void setup_signals(void); -int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t); +int make_outfile_name(char *buf, int max, int fnum, int cpu, + time_t t, int bulk); int init_backlog(int cpu); void write_backlog(int cpu, int fnum, time_t t); time_t read_backlog(int cpu, int fnum); diff --git a/runtime/sym.c b/runtime/sym.c index 63dad1af..386005b2 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -12,6 +12,7 @@ #ifndef _STP_SYM_C_ #define _STP_SYM_C_ +#include "sym.h" #include "string.c" #include "task_finder_vma.c" diff --git a/runtime/transport/control.c b/runtime/transport/control.c index 680d7306..35130f0f 100644 --- a/runtime/transport/control.c +++ b/runtime/transport/control.c @@ -9,6 +9,11 @@ * later version. */ +#include "control.h" +#include "../mempool.c" +#include "symbols.c" +#include <linux/delay.h> + static _stp_mempool_t *_stp_pool_q; static struct list_head _stp_ctl_ready_q; static DEFINE_SPINLOCK(_stp_ctl_ready_lock); @@ -32,7 +37,8 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz #ifdef DEBUG_TRANS if (type < STP_MAX_CMD) - _dbug("Got %s. len=%d\n", _stp_command_name[type], (int)count); + dbug_trans2("Got %s. len=%d\n", _stp_command_name[type], + (int)count); #endif switch (type) { @@ -79,27 +85,46 @@ static void _stp_ctl_write_dbug(int type, void *data, int len) char buf[64]; switch (type) { case STP_START: - _dbug("sending STP_START\n"); + dbug_trans2("sending STP_START\n"); break; case STP_EXIT: - _dbug("sending STP_EXIT\n"); + dbug_trans2("sending STP_EXIT\n"); break; case STP_OOB_DATA: snprintf(buf, sizeof(buf), "%s", (char *)data); - _dbug("sending %d bytes of STP_OOB_DATA: %s\n", len, buf); + dbug_trans2("sending %d bytes of STP_OOB_DATA: %s\n", len, + buf); break; case STP_SYSTEM: snprintf(buf, sizeof(buf), "%s", (char *)data); - _dbug("sending STP_SYSTEM: %s\n", buf); + dbug_trans2("sending STP_SYSTEM: %s\n", buf); break; case STP_TRANSPORT: - _dbug("sending STP_TRANSPORT\n"); + dbug_trans2("sending STP_TRANSPORT\n"); + break; + case STP_CONNECT: + dbug_trans2("sending STP_CONNECT\n"); + break; + case STP_DISCONNECT: + dbug_trans2("sending STP_DISCONNECT\n"); + break; + case STP_BULK: + dbug_trans2("sending STP_BULK\n"); + break; + case STP_READY: + case STP_RELOCATION: + case STP_BUF_INFO: + case STP_SUBBUFS_CONSUMED: + dbug_trans2("sending old message\n"); + break; + case STP_REALTIME_DATA: + dbug_trans2("sending %d bytes of STP_REALTIME_DATA\n", len); break; case STP_REQUEST_EXIT: - _dbug("sending STP_REQUEST_EXIT\n"); + dbug_trans2("sending STP_REQUEST_EXIT\n"); break; default: - _dbug("ERROR: unknown message type: %d\n", type); + dbug_trans2("ERROR: unknown message type: %d\n", type); break; } } @@ -197,7 +222,7 @@ static ssize_t _stp_ctl_read_cmd(struct file *file, char __user *buf, static int _stp_ctl_open_cmd(struct inode *inode, struct file *file) { - if (_stp_attached) + if (_stp_ctl_attached) return -1; _stp_attach(); return 0; @@ -205,7 +230,7 @@ static int _stp_ctl_open_cmd(struct inode *inode, struct file *file) static int _stp_ctl_close_cmd(struct inode *inode, struct file *file) { - if (_stp_attached) + if (_stp_ctl_attached) _stp_detach(); return 0; } diff --git a/runtime/transport/control.h b/runtime/transport/control.h index 5e7204ee..48289276 100644 --- a/runtime/transport/control.h +++ b/runtime/transport/control.h @@ -15,7 +15,6 @@ #include <linux/spinlock.h> #include <linux/list.h> -static _stp_mempool_t *_stp_pool_q; static struct list_head _stp_ctl_ready_q; static spinlock_t _stp_ctl_ready_lock; static wait_queue_head_t _stp_ctl_wq; diff --git a/runtime/transport/debugfs.c b/runtime/transport/debugfs.c index 85ee604d..6bbef53b 100644 --- a/runtime/transport/debugfs.c +++ b/runtime/transport/debugfs.c @@ -10,6 +10,7 @@ */ #include <linux/debugfs.h> +#include "transport.h" #define STP_DEFAULT_BUFFERS 50 @@ -22,18 +23,26 @@ static struct dentry *_stp_cmd_file = NULL; static int _stp_register_ctl_channel_fs(void) { - if (_stp_utt == NULL) { - errk("_expected _stp_utt to be set.\n"); + struct dentry *module_dir = _stp_get_module_dir(); + if (module_dir == NULL) { + errk("no module directory found.\n"); return -1; } /* create [debugfs]/systemtap/module_name/.cmd */ - _stp_cmd_file = debugfs_create_file(".cmd", 0600, _stp_utt->dir, + _stp_cmd_file = debugfs_create_file(".cmd", 0600, module_dir, NULL, &_stp_ctl_fops_cmd); if (_stp_cmd_file == NULL) { errk("Error creating systemtap debugfs entries.\n"); return -1; } + else if (IS_ERR(_stp_cmd_file)) { + _stp_cmd_file = NULL; + errk("Error creating systemtap debugfs entries: %ld\n", + -PTR_ERR(_stp_cmd_file)); + return -1; + } + _stp_cmd_file->d_inode->i_uid = _stp_uid; _stp_cmd_file->d_inode->i_gid = _stp_gid; diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c index 6afbdea1..9e05cc14 100644 --- a/runtime/transport/procfs.c +++ b/runtime/transport/procfs.c @@ -14,7 +14,6 @@ #define STP_DEFAULT_BUFFERS 256 #ifdef STP_BULKMODE -extern int _stp_relay_flushing; /* handle the per-cpu subbuf info read for relayfs */ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -23,13 +22,13 @@ static ssize_t _stp_proc_read(struct file *file, char __user *buf, size_t count, int cpu = *(int *)(PDE(file->f_dentry->d_inode)->data); - if (!_stp_utt->rchan) + if (!_stp_relay_data.rchan) return -EINVAL; out.cpu = cpu; - out.produced = atomic_read(&_stp_utt->rchan->buf[cpu]->subbufs_produced); - out.consumed = atomic_read(&_stp_utt->rchan->buf[cpu]->subbufs_consumed); - out.flushing = _stp_relay_flushing; + out.produced = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_produced); + out.consumed = atomic_read(&_stp_relay_data.rchan->buf[cpu]->subbufs_consumed); + out.flushing = _stp_relay_data.flushing; num = sizeof(out); if (copy_to_user(buf, &out, num)) @@ -46,7 +45,7 @@ static ssize_t _stp_proc_write(struct file *file, const char __user *buf, size_t if (copy_from_user(&info, buf, count)) return -EFAULT; - relay_subbufs_consumed(_stp_utt->rchan, cpu, info.consumed); + relay_subbufs_consumed(_stp_relay_data.rchan, cpu, info.consumed); return count; } diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c new file mode 100644 index 00000000..65e9c59b --- /dev/null +++ b/runtime/transport/relay_v2.c @@ -0,0 +1,384 @@ +/* -*- linux-c -*- + * + * This transport version uses relayfs on top of a debugfs file. This + * code started as a proposed relayfs interface called 'utt'. It has + * been modified and simplified for systemtap. + * + * Changes Copyright (C) 2009 Red Hat Inc. + * + * Original utt code by: + * Copyright (C) 2006 Jens Axboe <axboe@suse.de> + * Moved to utt.c by Tom Zanussi, 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/percpu.h> +#include <linux/init.h> +#include <linux/debugfs.h> +#include <linux/mm.h> +#include <linux/relay.h> +#include <linux/timer.h> + +#ifndef STP_RELAY_TIMER_INTERVAL +/* Wakeup timer interval in jiffies (default 10 ms) */ +#define STP_RELAY_TIMER_INTERVAL ((HZ + 99) / 100) +#endif + +struct _stp_relay_data_type { + enum _stp_transport_state transport_state; + struct rchan *rchan; + struct dentry *dropped_file; + atomic_t dropped; + atomic_t wakeup; + struct timer_list timer; + int overwrite_flag; +}; +struct _stp_relay_data_type _stp_relay_data; + +/* + * __stp_relay_switch_subbuf - switch to a new sub-buffer + * + * Most of this function is deadcopy of relay_switch_subbuf. + */ +static size_t __stp_relay_switch_subbuf(struct rchan_buf *buf, size_t length) +{ + char *old, *new; + size_t old_subbuf, new_subbuf; + + if (unlikely(buf == NULL)) + return 0; + + if (unlikely(length > buf->chan->subbuf_size)) + goto toobig; + + if (buf->offset != buf->chan->subbuf_size + 1) { + buf->prev_padding = buf->chan->subbuf_size - buf->offset; + old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; + buf->padding[old_subbuf] = buf->prev_padding; + buf->subbufs_produced++; + buf->dentry->d_inode->i_size += buf->chan->subbuf_size - + buf->padding[old_subbuf]; + smp_mb(); + if (waitqueue_active(&buf->read_wait)) + /* + * Calling wake_up_interruptible() and __mod_timer() + * from here will deadlock if we happen to be logging + * from the scheduler and timer (trying to re-grab + * rq->lock/timer->base->lock), so just set a flag. + */ + atomic_set(&_stp_relay_data.wakeup, 1); + } + + old = buf->data; + new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; + new = (char*)buf->start + new_subbuf * buf->chan->subbuf_size; + buf->offset = 0; + if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { + buf->offset = buf->chan->subbuf_size + 1; + return 0; + } + buf->data = new; + buf->padding[new_subbuf] = 0; + + if (unlikely(length + buf->offset > buf->chan->subbuf_size)) + goto toobig; + + return length; + +toobig: + buf->chan->last_toobig = length; + return 0; +} + +static void __stp_relay_wakeup_readers(struct rchan_buf *buf) +{ + if (buf && waitqueue_active(&buf->read_wait) && + buf->subbufs_produced != buf->subbufs_consumed) + wake_up_interruptible(&buf->read_wait); +} + +static void __stp_relay_wakeup_timer(unsigned long val) +{ +#ifdef STP_BULKMODE + int i; +#endif + + if (atomic_read(&_stp_relay_data.wakeup)) { + atomic_set(&_stp_relay_data.wakeup, 0); +#ifdef STP_BULKMODE + for_each_possible_cpu(i) + __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[i]); +#else + __stp_relay_wakeup_readers(_stp_relay_data.rchan->buf[0]); +#endif + } + + mod_timer(&_stp_relay_data.timer, jiffies + STP_RELAY_TIMER_INTERVAL); +} + +static void __stp_relay_timer_init(void) +{ + atomic_set(&_stp_relay_data.wakeup, 0); + init_timer(&_stp_relay_data.timer); + _stp_relay_data.timer.expires = jiffies + STP_RELAY_TIMER_INTERVAL; + _stp_relay_data.timer.function = __stp_relay_wakeup_timer; + _stp_relay_data.timer.data = 0; + add_timer(&_stp_relay_data.timer); + smp_mb(); +} + +static enum _stp_transport_state _stp_transport_get_state(void) +{ + return _stp_relay_data.transport_state; +} + +static void _stp_transport_data_fs_overwrite(int overwrite) +{ + _stp_relay_data.overwrite_flag = overwrite; +} + +static int __stp_relay_dropped_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static ssize_t __stp_relay_dropped_read(struct file *filp, char __user *buffer, + size_t count, loff_t *ppos) +{ + char buf[16]; + + snprintf(buf, sizeof(buf), "%u\n", + atomic_read(&_stp_relay_data.dropped)); + + return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); +} + +static struct file_operations __stp_relay_dropped_fops = { + .owner = THIS_MODULE, + .open = __stp_relay_dropped_open, + .read = __stp_relay_dropped_read, +}; + +/* + * Keep track of how many times we encountered a full subbuffer, to aid + * the user space app in telling how many lost events there were. + */ +static int __stp_relay_subbuf_start_callback(struct rchan_buf *buf, + void *subbuf, void *prev_subbuf, + size_t prev_padding) +{ + if (_stp_relay_data.overwrite_flag || !relay_buf_full(buf)) + return 1; + + atomic_inc(&_stp_relay_data.dropped); + return 0; +} + +static int __stp_relay_remove_buf_file_callback(struct dentry *dentry) +{ + debugfs_remove(dentry); + return 0; +} + +static struct dentry * +__stp_relay_create_buf_file_callback(const char *filename, + struct dentry *parent, + int mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + /* + * Here's what 'is_global' does (from linux/relay.h): + * + * Setting the is_global outparam to a non-zero value will + * cause relay_open() to create a single global buffer rather + * than the default set of per-cpu buffers. + */ + if (is_global) { +#ifdef STP_BULKMODE + *is_global = 0; +#else + *is_global = 1; +#endif + } + + if (IS_ERR(file)) { + file = NULL; + } + else if (file) { + file->d_inode->i_uid = _stp_uid; + file->d_inode->i_gid = _stp_gid; + } + return file; +} + +static struct rchan_callbacks __stp_relay_callbacks = { + .subbuf_start = __stp_relay_subbuf_start_callback, + .create_buf_file = __stp_relay_create_buf_file_callback, + .remove_buf_file = __stp_relay_remove_buf_file_callback, +}; + +static void _stp_transport_data_fs_start(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) { + /* We're initialized. Now start the timer. */ + __stp_relay_timer_init(); + _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING; + } +} + +static void _stp_transport_data_fs_stop(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) { + del_timer_sync(&_stp_relay_data.timer); + dbug_trans(0, "flushing...\n"); + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + if (_stp_relay_data.rchan) + relay_flush(_stp_relay_data.rchan); + } +} + +static void _stp_transport_data_fs_close(void) +{ + _stp_transport_data_fs_stop(); + if (_stp_relay_data.dropped_file) + debugfs_remove(_stp_relay_data.dropped_file); + if (_stp_relay_data.rchan) { + relay_close(_stp_relay_data.rchan); + _stp_relay_data.rchan = NULL; + } +} + +static int _stp_transport_data_fs_init(void) +{ + int rc; + u64 npages; + struct sysinfo si; + + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.overwrite_flag = 0; + atomic_set(&_stp_relay_data.dropped, 0); + _stp_relay_data.dropped_file = NULL; + _stp_relay_data.rchan = NULL; + + /* Create "dropped" file. */ + _stp_relay_data.dropped_file + = debugfs_create_file("dropped", 0444, _stp_get_module_dir(), + NULL, &__stp_relay_dropped_fops); + if (!_stp_relay_data.dropped_file) { + rc = -EIO; + goto err; + } + else if (IS_ERR(_stp_relay_data.dropped_file)) { + rc = PTR_ERR(_stp_relay_data.dropped_file); + _stp_relay_data.dropped_file = NULL; + goto err; + } + + _stp_relay_data.dropped_file->d_inode->i_uid = _stp_uid; + _stp_relay_data.dropped_file->d_inode->i_gid = _stp_gid; + + /* Create "trace" file. */ + npages = _stp_subbuf_size * _stp_nsubbufs; +#ifdef STP_BULKMODE + npages *= num_possible_cpus(); +#endif + npages >>= PAGE_SHIFT; + si_meminfo(&si); +#define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT)) + if (npages > (si.freeram + si.bufferram)) { + errk("Not enough free+buffered memory(%luMB) for log buffer(%luMB)\n", + MB(si.freeram + si.bufferram), + MB(npages)); + rc = -ENOMEM; + goto err; + } + else if (npages > si.freeram) { + /* exceeds freeram, but below freeram+bufferram */ + printk(KERN_WARNING + "log buffer size exceeds free memory(%luMB)\n", + MB(si.freeram)); + } +#if (RELAYFS_CHANNEL_VERSION >= 7) + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + &__stp_relay_callbacks, NULL); +#else /* (RELAYFS_CHANNEL_VERSION < 7) */ + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + &__stp_relay_callbacks); +#endif /* (RELAYFS_CHANNEL_VERSION < 7) */ + if (!_stp_relay_data.rchan) { + rc = -ENOENT; + goto err; + } + dbug_trans(1, "returning 0...\n"); + _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED; + + return 0; + +err: + _stp_transport_data_fs_close(); + return rc; +} + + +/** + * _stp_data_write_reserve - try to reserve size_request bytes + * @size_request: number of bytes to attempt to reserve + * @entry: entry is returned here + * + * Returns number of bytes reserved, 0 if full. On return, entry + * will point to allocated opaque pointer. Use + * _stp_data_entry_data() to get pointer to copy data into. + * + * (For this code's purposes, entry is filled in with the actual + * data pointer, but the caller doesn't know that.) + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) +{ + struct rchan_buf *buf; + + if (entry == NULL) + return -EINVAL; + + buf = _stp_relay_data.rchan->buf[smp_processor_id()]; + if (unlikely(buf->offset + size_request > buf->chan->subbuf_size)) { + size_request = __stp_relay_switch_subbuf(buf, size_request); + if (!size_request) + return 0; + } + *entry = (char*)buf->data + buf->offset; + buf->offset += size_request; + + return size_request; +} + +static unsigned char *_stp_data_entry_data(void *entry) +{ + /* Nothing to do here. */ + return entry; +} + +static int _stp_data_write_commit(void *entry) +{ + /* Nothing to do here. */ + return 0; +} diff --git a/runtime/transport/relayfs.c b/runtime/transport/relayfs.c index 6eefda8d..338b2771 100644 --- a/runtime/transport/relayfs.c +++ b/runtime/transport/relayfs.c @@ -22,17 +22,27 @@ #include <linux/init.h> #include <linux/relayfs_fs.h> #include <linux/namei.h> -#include "utt.h" -static int _stp_relay_flushing = 0; +struct _stp_relay_data_type { + enum _stp_transport_state transport_state; + struct rchan *rchan; + int flushing; +}; +struct _stp_relay_data_type _stp_relay_data; + +/* We need to include procfs.c here so that it can see the + * _stp_relay_data_type definition. */ +#include "procfs.c" /** - * _stp_subbuf_start - subbuf_start() relayfs callback implementation + * __stp_relay_subbuf_start_callback - subbuf_start() relayfs + * callback implementation */ -static int _stp_subbuf_start(struct rchan_buf *buf, - void *subbuf, - unsigned prev_subbuf_idx, - void *prev_subbuf) +static int +__stp_relay_subbuf_start_callback(struct rchan_buf *buf, + void *subbuf, + unsigned prev_subbuf_idx, + void *prev_subbuf) { unsigned padding = buf->padding[prev_subbuf_idx]; if (prev_subbuf) @@ -42,11 +52,12 @@ static int _stp_subbuf_start(struct rchan_buf *buf, } /** - * _stp_buf_full - buf_full() relayfs callback implementation + * __stp_relay_buf_full_callback - buf_full() relayfs callback + * implementation */ -static void _stp_buf_full(struct rchan_buf *buf, - unsigned subbuf_idx, - void *subbuf) +static void __stp_relay_buf_full_callback(struct rchan_buf *buf, + unsigned subbuf_idx, + void *subbuf) { unsigned padding = buf->padding[subbuf_idx]; *((unsigned *)subbuf) = padding; @@ -54,126 +65,113 @@ static void _stp_buf_full(struct rchan_buf *buf, static struct rchan_callbacks stp_rchan_callbacks = { - .subbuf_start = _stp_subbuf_start, - .buf_full = _stp_buf_full, + .subbuf_start = __stp_relay_subbuf_start_callback, + .buf_full = __stp_relay_buf_full_callback, }; +static void _stp_transport_data_fs_start(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) + _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING; +} -static void _stp_remove_relay_dir(struct dentry *dir) +static void _stp_transport_data_fs_stop(void) { - if (dir) - relayfs_remove_dir(dir); + if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) { + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.flushing = 1; + if (_stp_relay_data.rchan) + relay_flush(_stp_relay_data.rchan); + } } -static void _stp_remove_relay_root(struct dentry *root) +static void _stp_transport_data_fs_close(void) { - if (root) { - if (!_stp_lock_transport_dir()) { - errk("Unable to lock transport directory.\n"); - return; - } - _stp_remove_relay_dir(root); - _stp_unlock_transport_dir(); + _stp_transport_data_fs_stop(); + if (_stp_relay_data.rchan) { + relay_close(_stp_relay_data.rchan); + _stp_relay_data.rchan = NULL; } } -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts) +static int _stp_transport_data_fs_init(void) { - struct utt_trace *utt; + int rc = 0; int i; - utt = _stp_kzalloc(sizeof(*utt)); - if (!utt) - return NULL; - - utt->utt_tree_root = _stp_get_root_dir(utts->root); - if (!utt->utt_tree_root) - goto err; - - utt->dir = relayfs_create_dir(utts->name, utt->utt_tree_root); - if (!utt->dir) - goto err; + dbug_trans(1, "relay_open %d %d\n", _stp_subbuf_size, _stp_nsubbufs); + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.flushing = 0; - dbug_trans(1, "relay_open %d %d\n", utts->buf_size, utts->buf_nr); - - utt->rchan = relay_open("trace", utt->dir, utts->buf_size, - utts->buf_nr, 0, &stp_rchan_callbacks); - if (!utt->rchan) + /* Create "trace" file. */ + _stp_relay_data.rchan = relay_open("trace", _stp_get_module_dir(), + _stp_subbuf_size, _stp_nsubbufs, + 0, &stp_rchan_callbacks); + if (!_stp_relay_data.rchan) { + rc = -ENOENT; goto err; + } /* now set ownership */ for_each_online_cpu(i) { - utt->rchan->buf[i]->dentry->d_inode->i_uid = _stp_uid; - utt->rchan->buf[i]->dentry->d_inode->i_gid = _stp_gid; + _stp_relay_data.rchan->buf[i]->dentry->d_inode->i_uid + = _stp_uid; + _stp_relay_data.rchan->buf[i]->dentry->d_inode->i_gid + = _stp_gid; } - utt->rchan->private_data = utt; - utt->trace_state = Utt_trace_setup; - utts->err = 0; - return utt; + /* We're initialized. */ + _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED; + return rc; err: errk("couldn't create relay channel.\n"); - if (utt->dir) - _stp_remove_relay_dir(utt->dir); - if (utt->utt_tree_root) - _stp_remove_relay_root(utt->utt_tree_root); - _stp_kfree(utt); - return NULL; + _stp_transport_data_fs_close(); + return rc; } -static void utt_set_overwrite(int overwrite) +static enum _stp_transport_state _stp_transport_get_state(void) { - if (_stp_utt) - _stp_utt->rchan->overwrite = overwrite; + return _stp_relay_data.transport_state; } -static int utt_trace_startstop(struct utt_trace *utt, int start, - unsigned int *trace_seq) +static void _stp_transport_data_fs_overwrite(int overwrite) { - int ret; - - if (!utt) - return 0; + _stp_relay_data.rchan->overwrite = overwrite; +} - /* - * For starting a trace, we can transition from a setup or stopped - * trace. For stopping a trace, the state must be running - */ - ret = -EINVAL; - if (start) { - if (utt->trace_state == Utt_trace_setup || - utt->trace_state == Utt_trace_stopped) { - if (trace_seq) - (*trace_seq)++; - smp_mb(); - utt->trace_state = Utt_trace_running; - ret = 0; - } - } else { - if (utt->trace_state == Utt_trace_running) { - utt->trace_state = Utt_trace_stopped; - _stp_relay_flushing = 1; - relay_flush(utt->rchan); - ret = 0; - } - } +/** + * _stp_data_write_reserve - try to reserve size_request bytes + * @size_request: number of bytes to attempt to reserve + * @entry: entry is returned here + * + * Returns number of bytes reserved, 0 if full. On return, entry + * will point to allocated opaque pointer. Use + * _stp_data_entry_data() to get pointer to copy data into. + * + * (For this code's purposes, entry is filled in with the actual + * data pointer, but the caller doesn't know that.) + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) +{ + if (entry == NULL) + return -EINVAL; - return ret; + *entry = relay_reserve(_stp_relay_data.rchan, size_request); + if (*entry == NULL) + return 0; + return size_request; } +static unsigned char *_stp_data_entry_data(void *entry) +{ + /* Nothing to do here. */ + return entry; +} -static int utt_trace_remove(struct utt_trace *utt) +static int _stp_data_write_commit(void *entry) { - dbug_trans(1, "removing relayfs files. %d\n", utt->trace_state); - if (utt && (utt->trace_state == Utt_trace_setup || utt->trace_state == Utt_trace_stopped)) { - if (utt->rchan) - relay_close(utt->rchan); - if (utt->dir) - _stp_remove_relay_dir(utt->dir); - if (utt->utt_tree_root) - _stp_remove_relay_root(utt->utt_tree_root); - _stp_kfree(utt); - } + /* Nothing to do here. */ return 0; } diff --git a/runtime/transport/ring_buffer.c b/runtime/transport/ring_buffer.c new file mode 100644 index 00000000..0b73d4b4 --- /dev/null +++ b/runtime/transport/ring_buffer.c @@ -0,0 +1,472 @@ +#include <linux/types.h> +#include <linux/ring_buffer.h> +#include <linux/wait.h> +#include <linux/poll.h> +#include <linux/cpumask.h> + +struct _stp_data_entry { + size_t len; + unsigned char buf[]; +}; + +/* + * Trace iterator - used by printout routines who present trace + * results to users and which routines might sleep, etc: + */ +struct _stp_ring_buffer_data { + int cpu; + u64 ts; +}; + +struct _stp_relay_data_type { + enum _stp_transport_state transport_state; + struct ring_buffer *rb; + struct _stp_ring_buffer_data rb_data; + cpumask_var_t trace_reader_cpumask; +}; +static struct _stp_relay_data_type _stp_relay_data; + +/* _stp_poll_wait is a waitqueue for tasks blocked on + * _stp_data_poll_trace() */ +static DECLARE_WAIT_QUEUE_HEAD(_stp_poll_wait); + +static void __stp_free_ring_buffer(void) +{ + free_cpumask_var(_stp_relay_data.trace_reader_cpumask); + if (_stp_relay_data.rb) + ring_buffer_free(_stp_relay_data.rb); + _stp_relay_data.rb = NULL; +} + +static int __stp_alloc_ring_buffer(void) +{ + int i; + unsigned long buffer_size = _stp_bufsize; + + if (!alloc_cpumask_var(&_stp_relay_data.trace_reader_cpumask, + GFP_KERNEL)) + goto fail; + cpumask_clear(_stp_relay_data.trace_reader_cpumask); + + if (buffer_size == 0) { + dbug_trans(1, "using default buffer size...\n"); + buffer_size = _stp_nsubbufs * _stp_subbuf_size; + } + /* The number passed to ring_buffer_alloc() is per cpu. Our + * 'buffer_size' is a total number of bytes to allocate. So, + * we need to divide buffer_size by the number of cpus. */ + buffer_size /= num_online_cpus(); + dbug_trans(1, "%lu\n", buffer_size); + _stp_relay_data.rb = ring_buffer_alloc(buffer_size, 0); + if (!_stp_relay_data.rb) + goto fail; + + dbug_trans(1, "size = %lu\n", ring_buffer_size(_stp_relay_data.rb)); + return 0; + +fail: + __stp_free_ring_buffer(); + return -ENOMEM; +} + +static int _stp_data_open_trace(struct inode *inode, struct file *file) +{ + long cpu_file = (long) inode->i_private; + + /* We only allow for one reader per cpu */ + dbug_trans(1, "trace attach\n"); +#ifdef STP_BULKMODE + if (!cpumask_test_cpu(cpu_file, _stp_relay_data.trace_reader_cpumask)) + cpumask_set_cpu(cpu_file, _stp_relay_data.trace_reader_cpumask); + else { + dbug_trans(1, "returning EBUSY\n"); + return -EBUSY; + } +#else + if (!cpumask_empty(_stp_relay_data.trace_reader_cpumask)) { + dbug_trans(1, "returning EBUSY\n"); + return -EBUSY; + } + cpumask_setall(_stp_relay_data.trace_reader_cpumask); +#endif + file->private_data = inode->i_private; + return 0; +} + +static int _stp_data_release_trace(struct inode *inode, struct file *file) +{ + long cpu_file = (long) inode->i_private; + dbug_trans(1, "trace detach\n"); +#ifdef STP_BULKMODE + cpumask_clear_cpu(cpu_file, _stp_relay_data.trace_reader_cpumask); +#else + cpumask_clear(_stp_relay_data.trace_reader_cpumask); +#endif + + return 0; +} + +size_t +_stp_event_to_user(struct ring_buffer_event *event, char __user *ubuf, + size_t cnt) +{ + int ret; + struct _stp_data_entry *entry; + + dbug_trans(1, "event(%p), ubuf(%p), cnt(%lu)\n", event, ubuf, cnt); + if (event == NULL || ubuf == NULL) + return -EFAULT; + + entry = (struct _stp_data_entry *)ring_buffer_event_data(event); + if (entry == NULL) + return -EFAULT; + + /* We don't do partial entries - just fail. */ + if (entry->len > cnt) + return -EBUSY; + + if (cnt > entry->len) + cnt = entry->len; + ret = copy_to_user(ubuf, entry->buf, cnt); + if (ret) + return -EFAULT; + + return cnt; +} + +static ssize_t tracing_wait_pipe(struct file *filp) +{ + while (ring_buffer_empty(_stp_relay_data.rb)) { + + if ((filp->f_flags & O_NONBLOCK)) { + dbug_trans(1, "returning -EAGAIN\n"); + return -EAGAIN; + } + + /* + * This is a make-shift waitqueue. The reason we don't use + * an actual wait queue is because: + * 1) we only ever have one waiter + * 2) the tracing, traces all functions, we don't want + * the overhead of calling wake_up and friends + * (and tracing them too) + * Anyway, this is really very primitive wakeup. + */ + set_current_state(TASK_INTERRUPTIBLE); + + /* sleep for 100 msecs, and try again. */ + schedule_timeout(HZ/10); + + if (signal_pending(current)) { + dbug_trans(1, "returning -EINTR\n"); + return -EINTR; + } + } + + dbug_trans(1, "returning 1\n"); + return 1; +} + +static struct ring_buffer_event * +peek_next_event(int cpu, u64 *ts) +{ + return ring_buffer_peek(_stp_relay_data.rb, cpu, ts); +} + +/* Find the next real event */ +static struct ring_buffer_event * +_stp_find_next_event(long cpu_file) +{ + struct ring_buffer_event *event; + +#ifdef STP_BULKMODE + /* + * If we are in a per_cpu trace file, don't bother by iterating over + * all cpus and peek directly. + */ + if (ring_buffer_empty_cpu(_stp_relay_data.rb, (int)cpu_file)) + return NULL; + event = peek_next_event(cpu_file, &_stp_relay_data.rb_data.ts); + _stp_relay_data.rb_data.cpu = cpu_file; + + return event; +#else + struct ring_buffer_event *next = NULL; + u64 next_ts = 0, ts; + int next_cpu = -1; + int cpu; + + for_each_possible_cpu(cpu) { + + if (ring_buffer_empty_cpu(_stp_relay_data.rb, cpu)) + continue; + + event = peek_next_event(cpu, &ts); + + /* + * Pick the event with the smallest timestamp: + */ + if (event && (!next || ts < next_ts)) { + next = event; + next_cpu = cpu; + next_ts = ts; + } + } + + _stp_relay_data.rb_data.cpu = next_cpu; + _stp_relay_data.rb_data.ts = next_ts; + + return next; +#endif +} + + +/* + * Consumer reader. + */ +static ssize_t +_stp_data_read_trace(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + ssize_t sret; + struct ring_buffer_event *event; + long cpu_file = (long) filp->private_data; + + dbug_trans(1, "%lu\n", (unsigned long)cnt); + + sret = tracing_wait_pipe(filp); + dbug_trans(1, "tracing_wait_pipe returned %ld\n", sret); + if (sret <= 0) + goto out; + + /* stop when tracing is finished */ + if (ring_buffer_empty(_stp_relay_data.rb)) { + sret = 0; + goto out; + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + dbug_trans(1, "sret = %lu\n", (unsigned long)sret); + sret = 0; + while ((event = _stp_find_next_event(cpu_file)) != NULL) { + ssize_t len; + + len = _stp_event_to_user(event, ubuf, cnt); + if (len <= 0) + break; + + ring_buffer_consume(_stp_relay_data.rb, + _stp_relay_data.rb_data.cpu, + &_stp_relay_data.rb_data.ts); + ubuf += len; + cnt -= len; + sret += len; + if (cnt <= 0) + break; + } +out: + return sret; +} + + +static unsigned int +_stp_data_poll_trace(struct file *filp, poll_table *poll_table) +{ + dbug_trans(1, "entry\n"); + if (! ring_buffer_empty(_stp_relay_data.rb)) + return POLLIN | POLLRDNORM; + poll_wait(filp, &_stp_poll_wait, poll_table); + if (! ring_buffer_empty(_stp_relay_data.rb)) + return POLLIN | POLLRDNORM; + + dbug_trans(1, "exit\n"); + return 0; +} + +static struct file_operations __stp_data_fops = { + .owner = THIS_MODULE, + .open = _stp_data_open_trace, + .release = _stp_data_release_trace, + .poll = _stp_data_poll_trace, + .read = _stp_data_read_trace, +#if 0 + .splice_read = tracing_splice_read_pipe, +#endif +}; + +/* + * Here's how __STP_MAX_RESERVE_SIZE is figured. The value of + * BUF_PAGE_SIZE was gotten from the kernel's ring_buffer code. It + * is divided by 4, so we waste a maximum of 1/4 of the buffer (in + * the case of a small reservation). + */ +#define __STP_MAX_RESERVE_SIZE ((/*BUF_PAGE_SIZE*/ 4080 / 4) \ + - sizeof(struct _stp_data_entry) \ + - sizeof(struct ring_buffer_event)) + +/* + * This function prepares the cpu buffer to write a sample. + * + * Struct op_entry is used during operations on the ring buffer while + * struct op_sample contains the data that is stored in the ring + * buffer. Struct entry can be uninitialized. The function reserves a + * data array that is specified by size. Use + * op_cpu_buffer_write_commit() after preparing the sample. In case of + * errors a null pointer is returned, otherwise the pointer to the + * sample. + * + */ +static size_t +_stp_data_write_reserve(size_t size_request, void **entry) +{ + struct ring_buffer_event *event; + struct _stp_data_entry *sde; + + if (entry == NULL) + return -EINVAL; + + if (size_request > __STP_MAX_RESERVE_SIZE) { + size_request = __STP_MAX_RESERVE_SIZE; + } + + event = ring_buffer_lock_reserve(_stp_relay_data.rb, + sizeof(struct _stp_data_entry) + size_request, + 0); + if (unlikely(! event)) { + dbug_trans(1, "event = NULL (%p)?\n", event); + entry = NULL; + return 0; + } + + sde = (struct _stp_data_entry *)ring_buffer_event_data(event); + sde->len = size_request; + + *entry = event; + return size_request; +} + +static unsigned char *_stp_data_entry_data(void *entry) +{ + struct ring_buffer_event *event = entry; + struct _stp_data_entry *sde; + + if (event == NULL) + return NULL; + + sde = (struct _stp_data_entry *)ring_buffer_event_data(event); + return sde->buf; +} + +static int _stp_data_write_commit(void *entry) +{ + int ret; + struct ring_buffer_event *event = (struct ring_buffer_event *)entry; + + if (unlikely(! entry)) { + dbug_trans(1, "entry = NULL, returning -EINVAL\n"); + return -EINVAL; + } + + ret = ring_buffer_unlock_commit(_stp_relay_data.rb, event, 0); + dbug_trans(1, "after commit, empty returns %d\n", + ring_buffer_empty(_stp_relay_data.rb)); + + wake_up_interruptible(&_stp_poll_wait); + return ret; +} + + +static struct dentry *__stp_entry[NR_CPUS] = { NULL }; + +static int _stp_transport_data_fs_init(void) +{ + int rc; + long cpu; + + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + _stp_relay_data.rb = NULL; + + // allocate buffer + dbug_trans(1, "entry...\n"); + rc = __stp_alloc_ring_buffer(); + if (rc != 0) + return rc; + + // create file(s) + for_each_online_cpu(cpu) { + char cpu_file[9]; /* 5(trace) + 3(XXX) + 1(\0) = 9 */ + + if (cpu > 999 || cpu < 0) { + _stp_transport_data_fs_close(); + return -EINVAL; + } + sprintf(cpu_file, "trace%ld", cpu); + __stp_entry[cpu] = debugfs_create_file(cpu_file, 0600, + _stp_get_module_dir(), + (void *)cpu, + &__stp_data_fops); + + if (!__stp_entry[cpu]) { + pr_warning("Could not create debugfs 'trace' entry\n"); + __stp_free_ring_buffer(); + return -ENOENT; + } + else if (IS_ERR(__stp_entry[cpu])) { + rc = PTR_ERR(__stp_entry[cpu]); + pr_warning("Could not create debugfs 'trace' entry\n"); + __stp_free_ring_buffer(); + return rc; + } + + __stp_entry[cpu]->d_inode->i_uid = _stp_uid; + __stp_entry[cpu]->d_inode->i_gid = _stp_gid; + +#ifndef STP_BULKMODE + if (cpu != 0) + break; +#endif + } + + dbug_trans(1, "returning 0...\n"); + _stp_relay_data.transport_state = STP_TRANSPORT_INITIALIZED; + return 0; +} + +static void _stp_transport_data_fs_start(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_INITIALIZED) { + _stp_relay_data.transport_state = STP_TRANSPORT_RUNNING; + } +} + +static void _stp_transport_data_fs_stop(void) +{ + if (_stp_relay_data.transport_state == STP_TRANSPORT_RUNNING) { + _stp_relay_data.transport_state = STP_TRANSPORT_STOPPED; + } +} + +static void _stp_transport_data_fs_close(void) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (__stp_entry[cpu]) + debugfs_remove(__stp_entry[cpu]); + __stp_entry[cpu] = NULL; + } + + __stp_free_ring_buffer(); +} + +static enum _stp_transport_state _stp_transport_get_state(void) +{ + return _stp_relay_data.transport_state; +} + +static void _stp_transport_data_fs_overwrite(int overwrite) +{ + /* FIXME: Just a place holder for now. */ +} diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index 318c62f1..ec73f05f 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -14,27 +14,46 @@ #ifndef _TRANSPORT_TRANSPORT_C_ #define _TRANSPORT_TRANSPORT_C_ -#include <linux/delay.h> -#include <linux/namei.h> #include "transport.h" -#include "../mempool.c" -#include "symbols.c" +#include <linux/debugfs.h> +#include <linux/namei.h> +#include <linux/workqueue.h> +#include <linux/delay.h> -static struct utt_trace *_stp_utt = NULL; -static unsigned int utt_seq = 1; -static int _stp_probes_started = 0; -static pid_t _stp_target = 0; static int _stp_exit_flag = 0; + +static uid_t _stp_uid = 0; +static gid_t _stp_gid = 0; +static int _stp_pid = 0; + +static int _stp_ctl_attached = 0; + +static pid_t _stp_target = 0; +static int _stp_probes_started = 0; + +// For now, disable transport version 3 +#if STP_TRANSPORT_VERSION == 3 +#undef STP_TRANSPORT_VERSION +#define STP_TRANSPORT_VERSION 2 +#endif + #include "control.h" -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 #include "relayfs.c" -#include "procfs.c" -#else -#include "utt.c" +#elif STP_TRANSPORT_VERSION == 2 +#include "relay_v2.c" #include "debugfs.c" +#elif STP_TRANSPORT_VERSION == 3 +#include "ring_buffer.c" +#include "debugfs.c" +#else +#error "Unknown STP_TRANSPORT_VERSION" #endif #include "control.c" +static unsigned _stp_nsubbufs = 8; +static unsigned _stp_subbuf_size = 65536*4; + /* module parameters */ static int _stp_bufsize; module_param(_stp_bufsize, int, 0); @@ -47,7 +66,6 @@ MODULE_PARM_DESC(_stp_unprivileged_user, "user is unprivileged"); /* forward declarations */ static void probe_exit(void); static int probe_start(void); -static void _stp_exit(void); /* check for new workqueue API */ #ifdef DECLARE_DELAYED_WORK @@ -91,14 +109,15 @@ 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 called = 0; - if (!called) { + static int _stp_exit_called = 0; + + if (!_stp_exit_called) { int failures; dbug_trans(1, "cleanup_and_exit (%d)\n", send_exit); _stp_exit_flag = 1; /* we only want to do this stuff once */ - called = 1; + _stp_exit_called = 1; if (_stp_probes_started) { dbug_trans(1, "calling probe_exit\n"); @@ -111,9 +130,8 @@ static void _stp_cleanup_and_exit(int send_exit) if (failures) _stp_warn("There were %d transport failures.\n", failures); - dbug_trans(1, "************** calling startstop 0 *************\n"); - if (_stp_utt) - utt_trace_startstop(_stp_utt, 0, &utt_seq); + dbug_trans(1, "*** calling _stp_transport_data_fs_stop ***\n"); + _stp_transport_data_fs_stop(); dbug_trans(1, "ctl_send STP_EXIT\n"); if (send_exit) @@ -140,11 +158,11 @@ static void _stp_request_exit(void) static void _stp_detach(void) { dbug_trans(1, "detach\n"); - _stp_attached = 0; + _stp_ctl_attached = 0; _stp_pid = 0; if (!_stp_exit_flag) - utt_set_overwrite(1); + _stp_transport_data_fs_overwrite(1); cancel_delayed_work(&_stp_work); wake_up_interruptible(&_stp_ctl_wq); @@ -156,9 +174,9 @@ static void _stp_detach(void) static void _stp_attach(void) { dbug_trans(1, "attach\n"); - _stp_attached = 1; + _stp_ctl_attached = 1; _stp_pid = current->pid; - utt_set_overwrite(0); + _stp_transport_data_fs_overwrite(0); queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -185,7 +203,7 @@ static void _stp_work_queue(void *data) /* if exit flag is set AND we have finished with probe_start() */ if (unlikely(_stp_exit_flag && _stp_probes_started)) _stp_request_exit(); - if (likely(_stp_attached)) + if (likely(_stp_ctl_attached)) queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER); } @@ -195,34 +213,18 @@ static void _stp_work_queue(void *data) * This is called automatically when the module is unloaded. * */ -static void _stp_transport_close() +static void _stp_transport_close(void) { - dbug_trans(1, "%d: ************** transport_close *************\n", current->pid); + dbug_trans(1, "%d: ************** transport_close *************\n", + current->pid); _stp_cleanup_and_exit(0); destroy_workqueue(_stp_wq); _stp_unregister_ctl_channel(); - if (_stp_utt) - utt_trace_remove(_stp_utt); + _stp_transport_fs_close(); _stp_print_cleanup(); /* free print buffers */ _stp_mem_debug_done(); - dbug_trans(1, "---- CLOSED ----\n"); -} - -static struct utt_trace *_stp_utt_open(void) -{ - struct utt_trace_setup utts; - strlcpy(utts.root, "systemtap", sizeof(utts.root)); - strlcpy(utts.name, THIS_MODULE->name, sizeof(utts.name)); - utts.buf_size = _stp_subbuf_size; - utts.buf_nr = _stp_nsubbufs; - -#ifdef STP_BULKMODE - utts.is_global = 0; -#else - utts.is_global = 1; -#endif - return utt_trace_setup(&utts); + dbug_trans(1, "---- CLOSED ----\n"); } /** @@ -232,7 +234,6 @@ static struct utt_trace *_stp_utt_open(void) static int _stp_transport_init(void) { dbug_trans(1, "transport_init\n"); - _stp_init_pid = current->pid; #ifdef STAPCONF_TASK_UID _stp_uid = current->uid; _stp_gid = current->gid; @@ -258,12 +259,8 @@ static int _stp_transport_init(void) dbug_trans(1, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); } -#if !defined (STP_OLD_TRANSPORT) || defined (STP_BULKMODE) - /* open utt (relayfs) channel to send data to userspace */ - _stp_utt = _stp_utt_open(); - if (!_stp_utt) + if (_stp_transport_fs_init(THIS_MODULE->name) != 0) goto err0; -#endif /* create control channel */ if (_stp_register_ctl_channel() < 0) @@ -274,18 +271,17 @@ static int _stp_transport_init(void) goto err2; /* start transport */ - utt_trace_startstop(_stp_utt, 1, &utt_seq); + _stp_transport_data_fs_start(); /* create workqueue of kernel threads */ _stp_wq = create_workqueue("systemtap"); if (!_stp_wq) goto err3; - _stp_transport_state = 1; - /* Signal stapio to send us STP_START back (XXX: ?!?!?!). */ _stp_ctl_send(STP_TRANSPORT, NULL, 0); + dbug_trans(1, "returning 0...\n"); return 0; err3: @@ -293,8 +289,7 @@ err3: err2: _stp_unregister_ctl_channel(); err1: - if (_stp_utt) - utt_trace_remove(_stp_utt); + _stp_transport_fs_close(); err0: return -1; } @@ -322,7 +317,7 @@ static struct dentry *_stp_lockfile = NULL; static int _stp_lock_transport_dir(void) { int numtries = 0; -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 while ((_stp_lockfile = relayfs_create_dir("systemtap_lock", NULL)) == NULL) { #else while ((_stp_lockfile = debugfs_create_dir("systemtap_lock", NULL)) == NULL) { @@ -337,7 +332,7 @@ static int _stp_lock_transport_dir(void) static void _stp_unlock_transport_dir(void) { if (_stp_lockfile) { -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 relayfs_remove_dir(_stp_lockfile); #else debugfs_remove(_stp_lockfile); @@ -346,51 +341,151 @@ static void _stp_unlock_transport_dir(void) } } -/* _stp_get_root_dir(name) - creates root directory 'name' or */ -/* returns a pointer to it if it already exists. Used in */ -/* utt.c and relayfs.c. Will not be necessary if utt is included */ -/* in the kernel. */ +static struct dentry *__stp_root_dir = NULL; -static struct dentry *_stp_get_root_dir(const char *name) +/* _stp_get_root_dir() - creates root directory or returns + * a pointer to it if it already exists. */ + +static struct dentry *_stp_get_root_dir(void) { struct file_system_type *fs; - struct dentry *root; struct super_block *sb; + const char *name = "systemtap"; -#ifdef STP_OLD_TRANSPORT + if (__stp_root_dir != NULL) { + return __stp_root_dir; + } + +#if STP_TRANSPORT_VERSION == 1 fs = get_fs_type("relayfs"); + if (!fs) { + errk("Couldn't find relayfs filesystem.\n"); + return NULL; + } #else fs = get_fs_type("debugfs"); -#endif if (!fs) { - errk("Couldn't find debugfs or relayfs filesystem.\n"); + errk("Couldn't find debugfs filesystem.\n"); return NULL; } +#endif if (!_stp_lock_transport_dir()) { errk("Couldn't lock transport directory.\n"); return NULL; } -#ifdef STP_OLD_TRANSPORT - root = relayfs_create_dir(name, NULL); +#if STP_TRANSPORT_VERSION == 1 + __stp_root_dir = relayfs_create_dir(name, NULL); #else - root = debugfs_create_dir(name, NULL); + __stp_root_dir = debugfs_create_dir(name, NULL); #endif - if (!root) { - /* couldn't create it because it is already there, so find it. */ - sb = list_entry(fs->fs_supers.next, struct super_block, s_instances); + if (!__stp_root_dir) { + /* Couldn't create it because it is already there, so + * find it. */ + sb = list_entry(fs->fs_supers.next, struct super_block, + s_instances); _stp_lock_inode(sb->s_root->d_inode); - root = lookup_one_len(name, sb->s_root, strlen(name)); + __stp_root_dir = lookup_one_len(name, sb->s_root, + strlen(name)); _stp_unlock_inode(sb->s_root->d_inode); - if (!IS_ERR(root)) - dput(root); + if (!IS_ERR(__stp_root_dir)) + dput(__stp_root_dir); else { - root = NULL; + __stp_root_dir = NULL; errk("Could not create or find transport directory.\n"); } } + else if (IS_ERR(__stp_root_dir)) { + __stp_root_dir = NULL; + errk("Could not create root directory \"%s\", error %ld\n", name, + -PTR_ERR(__stp_root_dir)); + } + _stp_unlock_transport_dir(); - return root; + return __stp_root_dir; +} + +static void _stp_remove_root_dir(void) +{ + if (__stp_root_dir) { + if (!_stp_lock_transport_dir()) { + errk("Unable to lock transport directory.\n"); + return; + } + if (simple_empty(__stp_root_dir)) { +#if STP_TRANSPORT_VERSION == 1 + relayfs_remove_dir(__stp_root_dir); +#else + debugfs_remove(__stp_root_dir); +#endif + } + _stp_unlock_transport_dir(); + __stp_root_dir = NULL; + } +} + +static struct dentry *__stp_module_dir = NULL; + +static struct dentry *_stp_get_module_dir(void) +{ + return __stp_module_dir; +} + +static int _stp_transport_fs_init(const char *module_name) +{ + struct dentry *root_dir; + + dbug_trans(1, "entry\n"); + if (module_name == NULL) + return -1; + + root_dir = _stp_get_root_dir(); + if (root_dir == NULL) + return -1; + +#if STP_TRANSPORT_VERSION == 1 + __stp_module_dir = relayfs_create_dir(module_name, root_dir); +#else + __stp_module_dir = debugfs_create_dir(module_name, root_dir); +#endif + if (!__stp_module_dir) { + errk("Could not create module directory \"%s\"\n", + module_name); + _stp_remove_root_dir(); + return -1; + } + else if (IS_ERR(__stp_module_dir)) { + errk("Could not create module directory \"%s\", error %ld\n", + module_name, -PTR_ERR(__stp_module_dir)); + _stp_remove_root_dir(); + return -1; + } + + if (_stp_transport_data_fs_init() != 0) { + _stp_remove_root_dir(); + return -1; + } + dbug_trans(1, "returning 0\n"); + return 0; +} + +static void _stp_transport_fs_close(void) +{ + dbug_trans(1, "stp_transport_fs_close\n"); + + _stp_transport_data_fs_close(); + + if (__stp_module_dir) { +#if STP_TRANSPORT_VERSION == 1 + relayfs_remove_dir(__stp_module_dir); +#else + debugfs_remove(__stp_module_dir); +#endif + __stp_module_dir = NULL; + } + + _stp_remove_root_dir(); } + #endif /* _TRANSPORT_C_ */ diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index 7d249c45..871e37b3 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -11,9 +11,14 @@ /* amount of data a print can send. */ #define STP_BUFFER_SIZE 8192 +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. */ -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 /* Old transport sends print output on control channel */ #define STP_CTL_BUFFER_SIZE STP_BUFFER_SIZE #else @@ -23,27 +28,117 @@ /* how often the work queue wakes up and checks buffers */ #define STP_WORK_TIMER (HZ/100) -static unsigned _stp_nsubbufs = 8; -static unsigned _stp_subbuf_size = 65536*4; +static unsigned _stp_nsubbufs; +static unsigned _stp_subbuf_size; static int _stp_transport_init(void); static void _stp_transport_close(void); -static void _stp_warn (const char *fmt, ...); -static int _stp_print_init(void); -static void _stp_print_cleanup(void); -static struct dentry *_stp_get_root_dir(const char *name); - static int _stp_lock_transport_dir(void); static void _stp_unlock_transport_dir(void); +static struct dentry *_stp_get_root_dir(void); +static struct dentry *_stp_get_module_dir(void); + +static int _stp_transport_fs_init(const char *module_name); +static void _stp_transport_fs_close(void); + static void _stp_attach(void); static void _stp_detach(void); static void _stp_handle_start(struct _stp_msg_start *st); -static int _stp_pid = 0; -static uid_t _stp_uid = 0; -static gid_t _stp_gid = 0; -static pid_t _stp_init_pid = 0; -static int _stp_attached = 0; +static uid_t _stp_uid; +static gid_t _stp_gid; + +static int _stp_ctl_attached; + +static int _stp_bufsize; + + +enum _stp_transport_state { + STP_TRANSPORT_STOPPED, + STP_TRANSPORT_INITIALIZED, + STP_TRANSPORT_RUNNING, +}; + +/* + * All transports must provide the following functions. + */ + +/* + * _stp_transport_get_state + * + * This function returns the current transport state. + */ +static enum _stp_transport_state _stp_transport_get_state(void); + +/* + * _stp_transport_data_fs_init + * + * This function allocates any buffers needed, creates files, + * etc. needed for this transport. + */ +static int _stp_transport_data_fs_init(void); + +/* + * _stp_transport_data_fs_start + * + * This function actually starts the transport. + */ +static void _stp_transport_data_fs_start(void); + +/* + * _stp_transport_data_fs_start + * + * This function stops the transport without doing any cleanup. + */ +static void _stp_transport_data_fs_stop(void); + +/* + * _stp_transport_data_fs_close + * + * This function cleans up items created by + * _stp_transport_data_fs_init(). + */ +static void _stp_transport_data_fs_close(void); + +/* + * _stp_transport_data_fs_overwrite - set data overwrite mode + * overwrite: boolean + * + * When in overwrite mode and the transport buffers are full, older + * data gets overwritten. + */ +static void _stp_transport_data_fs_overwrite(int overwrite); + +/* + * _stp_data_write_reserve - reserve bytes + * size_request: number of bytes to reserve + * entry: allocated buffer is returned here + * + * This function attempts to reserve size_request number of bytes, + * returning the number of bytes actually reserved. The allocated + * buffer is returned in entry. Note that the number of bytes + * allocated may be less than the number of bytes requested. + */ +static size_t _stp_data_write_reserve(size_t size_request, void **entry); + + +/* + * _stp_data_entry_data - return data pointer from entry + * entry: entry + * + * This function returns the data pointer from entry. + */ +static unsigned char *_stp_data_entry_data(void *entry); + +/* + * _stp_data_write_commit - + * entry: pointer returned by _stp-data_write_reserve() + * + * This function notifies the transport that the bytes in entry are + * ready to be written. + */ +static int _stp_data_write_commit(void *entry); + #endif /* _TRANSPORT_TRANSPORT_H_ */ diff --git a/runtime/transport/transport_msgs.h b/runtime/transport/transport_msgs.h index 0d9a5983..aa50051c 100644 --- a/runtime/transport/transport_msgs.h +++ b/runtime/transport/transport_msgs.h @@ -29,8 +29,8 @@ enum STP_DISCONNECT, STP_BULK, STP_READY, - STP_RELOCATION, - /** deprecated STP_OLD_TRANSPORT **/ + STP_RELOCATION, + /** deprecated STP_TRANSPORT_VERSION == 1 **/ STP_BUF_INFO, STP_SUBBUFS_CONSUMED, STP_REALTIME_DATA, @@ -83,7 +83,7 @@ struct _stp_msg_start int32_t res; // for reply: result of probe_start() }; -#ifdef STP_OLD_TRANSPORT +#if STP_TRANSPORT_VERSION == 1 /**** for compatibility with old relayfs ****/ struct _stp_buf_info { diff --git a/runtime/transport/utt.c b/runtime/transport/utt.c deleted file mode 100644 index 915662b2..00000000 --- a/runtime/transport/utt.c +++ /dev/null @@ -1,406 +0,0 @@ -/* -*- linux-c -*- - * - * This is a modified version of the proposed utt interface. If that - * interface makes it into the kernel, this file can go away. - * - * Copyright (C) 2006 Jens Axboe <axboe@suse.de> - * - * Moved to utt.c by Tom Zanussi, 2006 - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/percpu.h> -#include <linux/init.h> -#include <linux/debugfs.h> -#include <linux/relay.h> -#include <linux/mm.h> -#include "utt.h" - -static int utt_overwrite_flag = 0; - -/* - * utt_switch_subbuf - switch to a new sub-buffer - * - * Most of this function is deadcopy of relay_switch_subbuf. - */ -static size_t utt_switch_subbuf(struct utt_trace *utt, struct rchan_buf *buf, - size_t length) -{ - char *old, *new; - size_t old_subbuf, new_subbuf; - - if (unlikely(buf == NULL)) - return 0; - - if (unlikely(length > buf->chan->subbuf_size)) - goto toobig; - - if (buf->offset != buf->chan->subbuf_size + 1) { - buf->prev_padding = buf->chan->subbuf_size - buf->offset; - old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; - buf->padding[old_subbuf] = buf->prev_padding; - buf->subbufs_produced++; - buf->dentry->d_inode->i_size += buf->chan->subbuf_size - - buf->padding[old_subbuf]; - smp_mb(); - if (waitqueue_active(&buf->read_wait)) - /* - * Calling wake_up_interruptible() and __mod_timer() - * from here will deadlock if we happen to be logging - * from the scheduler and timer (trying to re-grab - * rq->lock/timer->base->lock), so just set a flag. - */ - atomic_set(&utt->wakeup, 1); - } - - old = buf->data; - new_subbuf = buf->subbufs_produced % buf->chan->n_subbufs; - new = (char*)buf->start + new_subbuf * buf->chan->subbuf_size; - buf->offset = 0; - if (!buf->chan->cb->subbuf_start(buf, new, old, buf->prev_padding)) { - buf->offset = buf->chan->subbuf_size + 1; - return 0; - } - buf->data = new; - buf->padding[new_subbuf] = 0; - - if (unlikely(length + buf->offset > buf->chan->subbuf_size)) - goto toobig; - - return length; - -toobig: - buf->chan->last_toobig = length; - return 0; -} - -static void __utt_wakeup_readers(struct rchan_buf *buf) -{ - if (buf && waitqueue_active(&buf->read_wait) && - buf->subbufs_produced != buf->subbufs_consumed) - wake_up_interruptible(&buf->read_wait); -} - -static void __utt_wakeup_timer(unsigned long val) -{ - struct utt_trace *utt = (struct utt_trace *)val; - int i; - - if (atomic_read(&utt->wakeup)) { - atomic_set(&utt->wakeup, 0); - if (utt->is_global) - __utt_wakeup_readers(utt->rchan->buf[0]); - else - for_each_possible_cpu(i) - __utt_wakeup_readers(utt->rchan->buf[i]); - } - - mod_timer(&utt->timer, jiffies + UTT_TIMER_INTERVAL); -} - -static void __utt_timer_init(struct utt_trace * utt) -{ - atomic_set(&utt->wakeup, 0); - init_timer(&utt->timer); - utt->timer.expires = jiffies + UTT_TIMER_INTERVAL; - utt->timer.function = __utt_wakeup_timer; - utt->timer.data = (unsigned long)utt; - add_timer(&utt->timer); -} - -static void utt_set_overwrite(int overwrite) -{ - utt_overwrite_flag = overwrite; -} - -static void utt_remove_root(struct utt_trace *utt) -{ - if (utt->utt_tree_root) { - if (!_stp_lock_transport_dir()) { - errk("Unable to lock transport directory.\n"); - return; - } - if (simple_empty(utt->utt_tree_root)) - debugfs_remove(utt->utt_tree_root); - _stp_unlock_transport_dir(); - utt->utt_tree_root = NULL; - } -} - -static void utt_remove_tree(struct utt_trace *utt) -{ - if (utt == NULL || utt->dir == NULL) - return; - debugfs_remove(utt->dir); - utt_remove_root(utt); -} - -static struct dentry *utt_create_tree(struct utt_trace *utt, const char *root, const char *name) -{ - struct dentry *dir = NULL; - - if (root == NULL || name == NULL) - return NULL; - - if (!utt->utt_tree_root) { - utt->utt_tree_root = _stp_get_root_dir(root); - if (!utt->utt_tree_root) - goto err; - } - - dir = debugfs_create_dir(name, utt->utt_tree_root); - if (!dir) - utt_remove_root(utt); -err: - return dir; -} - - -static void utt_trace_cleanup(struct utt_trace *utt) -{ - if (utt == NULL) - return; - if (utt->rchan) - relay_close(utt->rchan); - if (utt->dropped_file) - debugfs_remove(utt->dropped_file); - utt_remove_tree(utt); - _stp_kfree(utt); -} - -static int utt_trace_remove(struct utt_trace *utt) -{ - if (utt->trace_state == Utt_trace_setup || - utt->trace_state == Utt_trace_stopped) - utt_trace_cleanup(utt); - - return 0; -} - -static int utt_dropped_open(struct inode *inode, struct file *filp) -{ -#ifdef STAPCONF_INODE_PRIVATE - filp->private_data = inode->i_private; -#else - filp->private_data = inode->u.generic_ip; -#endif - return 0; -} - -static ssize_t utt_dropped_read(struct file *filp, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct utt_trace *utt = filp->private_data; - char buf[16]; - - snprintf(buf, sizeof(buf), "%u\n", atomic_read(&utt->dropped)); - - return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); -} - -static struct file_operations utt_dropped_fops = { - .owner = THIS_MODULE, - .open = utt_dropped_open, - .read = utt_dropped_read, -}; - -/* - * Keep track of how many times we encountered a full subbuffer, to aid - * the user space app in telling how many lost events there were. - */ -static int utt_subbuf_start_callback(struct rchan_buf *buf, void *subbuf, - void *prev_subbuf, size_t prev_padding) -{ - struct utt_trace *utt; - - if (utt_overwrite_flag || !relay_buf_full(buf)) - return 1; - - utt = buf->chan->private_data; - atomic_inc(&utt->dropped); - return 0; -} - -static int utt_remove_buf_file_callback(struct dentry *dentry) -{ - debugfs_remove(dentry); - return 0; -} - -static struct dentry *utt_create_buf_file_callback(const char *filename, - struct dentry *parent, - int mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - if (file) { - file->d_inode->i_uid = _stp_uid; - file->d_inode->i_gid = _stp_gid; - } - return file; -} - -static struct dentry *utt_create_global_buf_file_callback(const char *filename, - struct dentry *parent, - int mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *file; - *is_global = 1; - file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - if (file) { - file->d_inode->i_uid = _stp_uid; - file->d_inode->i_gid = _stp_gid; - } - return file; -} - -static struct rchan_callbacks utt_relay_callbacks = { - .subbuf_start = utt_subbuf_start_callback, - .create_buf_file = utt_create_buf_file_callback, - .remove_buf_file = utt_remove_buf_file_callback, -}; - -static struct rchan_callbacks utt_relay_callbacks_global = { - .subbuf_start = utt_subbuf_start_callback, - .create_buf_file = utt_create_global_buf_file_callback, - .remove_buf_file = utt_remove_buf_file_callback, -}; - -/* - * Setup everything required to start tracing - */ -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts) -{ - struct utt_trace *utt = NULL; - struct dentry *dir = NULL; - int ret = -EINVAL; - u64 npages; - struct sysinfo si; - - if (!utts->buf_size || !utts->buf_nr) - goto err; - - ret = -ENOMEM; - utt = _stp_kzalloc(sizeof(*utt)); - if (!utt) - goto err; - - ret = -ENOENT; - dir = utt_create_tree(utt, utts->root, utts->name); - if (!dir) - goto err; - utt->dir = dir; - atomic_set(&utt->dropped, 0); - - ret = -EIO; - utt->dropped_file = debugfs_create_file("dropped", 0444, dir, utt, &utt_dropped_fops); - if (!utt->dropped_file) - goto err; - - npages = utts->buf_size * utts->buf_nr; - if (!utts->is_global) - npages *= num_possible_cpus(); - npages >>= PAGE_SHIFT; - si_meminfo(&si); -#define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT)) - if (npages > (si.freeram + si.bufferram)) { - errk("Not enough free+buffered memory(%luMB) for log buffer(%luMB)\n", - MB(si.freeram + si.bufferram), - MB(npages)); - ret = -ENOMEM; - goto err; - } else if (npages > si.freeram) { - /* exceeds freeram, but below freeram+bufferram */ - printk(KERN_WARNING - "log buffer size exceeds free memory(%luMB)\n", - MB(si.freeram)); - } - -#if (RELAYFS_CHANNEL_VERSION >= 7) - if (utts->is_global) - utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, - &utt_relay_callbacks_global, NULL); - else - utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, - &utt_relay_callbacks, NULL); -#else - if (utts->is_global) - utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, &utt_relay_callbacks_global); - else - utt->rchan = relay_open("trace", dir, utts->buf_size, utts->buf_nr, &utt_relay_callbacks); -#endif - - if (!utt->rchan) - goto err; - utt->rchan->private_data = utt; - - utt->is_global = utts->is_global; - - utt->trace_state = Utt_trace_setup; - - utts->err = 0; - return utt; -err: - if (utt) { - if (utt->dropped_file) - debugfs_remove(utt->dropped_file); - if (utt->rchan) - relay_close(utt->rchan); - _stp_kfree(utt); - } - if (dir) - utt_remove_tree(utt); - utts->err = ret; - return NULL; -} - -static int utt_trace_startstop(struct utt_trace *utt, int start, - unsigned int *trace_seq) -{ - int ret; - - /* - * For starting a trace, we can transition from a setup or stopped - * trace. For stopping a trace, the state must be running - */ - ret = -EINVAL; - if (start) { - if (utt->trace_state == Utt_trace_setup || - utt->trace_state == Utt_trace_stopped) { - if (trace_seq) - (*trace_seq)++; - __utt_timer_init(utt); - smp_mb(); - utt->trace_state = Utt_trace_running; - ret = 0; - } - } else { - if (utt->trace_state == Utt_trace_running) { - utt->trace_state = Utt_trace_stopped; - del_timer_sync(&utt->timer); - relay_flush(utt->rchan); - ret = 0; - } - } - - return ret; -} diff --git a/runtime/transport/utt.h b/runtime/transport/utt.h deleted file mode 100644 index 40e54919..00000000 --- a/runtime/transport/utt.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef UTT_H -#define UTT_H - -enum { - Utt_trace_setup = 1, - Utt_trace_running, - Utt_trace_stopped, -}; - -struct utt_trace { - int trace_state; - struct rchan *rchan; - struct dentry *dir; /* systemtap/module_name */ - struct dentry *dropped_file; - atomic_t dropped; - struct dentry *utt_tree_root; /* systemtap */ - void *private_data; - atomic_t wakeup; - struct timer_list timer; - int is_global; -}; - -#define UTT_TRACE_ROOT_NAME_SIZE 64 /* Largest string for a root dir identifier */ -#define UTT_TRACE_NAME_SIZE 64 /* Largest string for a trace identifier */ - -#ifndef UTT_TIMER_INTERVAL -#define UTT_TIMER_INTERVAL ((HZ + 99) / 100) /* Wakeup timer interval in jiffies (default 10 ms)*/ -#endif - -/* - * User setup structure - */ -struct utt_trace_setup { - char root[UTT_TRACE_ROOT_NAME_SIZE]; /* input */ - char name[UTT_TRACE_NAME_SIZE]; /* input */ - u32 buf_size; /* input */ - u32 buf_nr; /* input */ - int is_global; /* input */ - int err; /* output */ -}; - - -static struct utt_trace *utt_trace_setup(struct utt_trace_setup *utts); -static int utt_trace_startstop(struct utt_trace *utt, int start, - unsigned int *trace_seq); -static void utt_trace_cleanup(struct utt_trace *utt); -static int utt_trace_remove(struct utt_trace *utt); - -#ifndef STP_OLD_TRANSPORT -static size_t utt_switch_subbuf(struct utt_trace *utt, struct rchan_buf *buf, - size_t length); -/** - * utt_reserve - reserve slot in channel buffer - * @utt: utt channel - * @length: number of bytes to reserve - * - * Returns pointer to reserved slot, NULL if full. - * - * This function is utt_switch_subbuf version of relay_reserve. - */ -static inline void *utt_reserve(struct utt_trace *utt, size_t length) -{ - void *reserved; - struct rchan_buf *buf = utt->rchan->buf[smp_processor_id()]; - - if (unlikely(buf->offset + length > buf->chan->subbuf_size)) { - length = utt_switch_subbuf(utt, buf, length); - if (!length) - return NULL; - } - reserved = (char*)buf->data + buf->offset; - buf->offset += length; - - return reserved; -} -#endif - -#endif diff --git a/runtime/vsprintf.c b/runtime/vsprintf.c index 23810e75..5875d509 100644 --- a/runtime/vsprintf.c +++ b/runtime/vsprintf.c @@ -12,8 +12,8 @@ #ifndef _VSPRINTF_C_ #define _VSPRINTF_C_ -//forward declaration for _stp_vsnprintf -static void * _stp_reserve_bytes (int); +#include "print.h" +#include "transport/transport.h" static int skip_atoi(const char **s) { diff --git a/tapset/target_set.stp b/tapset/target_set.stp index c7878c52..9a4beced 100644 --- a/tapset/target_set.stp +++ b/tapset/target_set.stp @@ -3,25 +3,33 @@ global _target_set # map: target-set-pid -> ancestor-pid function target_set_pid (pid) { - return ([pid] in _target_set) + return ([pid] in _target_set) } probe begin { - if (target()) _target_set [target()] = stp_pid() + if (target()) + _target_set[target()] = stp_pid() } -probe syscall.fork.return +probe nd_syscall.fork.return { - pid=pid() - if (pid in _target_set) next - ppid=ppid() - if (ppid in _target_set) _target_set[pid]=ppid + pid = returnval() + if (pid in _target_set) + next + ppid = pid() + if (ppid in _target_set) + _target_set[pid] = ppid +} + +probe nd_syscall.exit +{ + delete _target_set[pid()] } function target_set_report () { - printf("target set:\n") - foreach (pid in _target_set+) - printf("%d begat %d\n", _target_set[pid], pid) + printf("target set:\n") + foreach (pid in _target_set+) + printf("%d begat %d\n", _target_set[pid], pid) } diff --git a/tapsets.cxx b/tapsets.cxx index 2c5e11bc..694905d9 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -1642,6 +1642,7 @@ query_cu (Dwarf_Die * cudie, void * arg) // statement. This is a somewhat lame check that the address // is at the start of an assembly instruction. Mark probes are in the // middle of a macro and thus not strictly at a statement beginning. + // Guru mode may override this check. if (q->has_statement_num && ! q->has_mark) { Dwarf_Addr queryaddr = q->statement_num_val; @@ -1654,7 +1655,12 @@ query_cu (Dwarf_Die * cudie, void * arg) stringstream msg; msg << "address 0x" << hex << queryaddr << " does not match the beginning of a statement"; - throw semantic_error(msg.str()); + if (address_line) + msg << " (try 0x" << hex << lineaddr << ")"; + if (! q->sess.guru_mode) + throw semantic_error(msg.str()); + else if (! q->sess.suppress_warnings) + q->sess.print_warning(msg.str()); } } // Pick up [entrypc, name, DIE] tuples for all the functions diff --git a/testsuite/buildok/null.stp b/testsuite/buildok/null.stp new file mode 100755 index 00000000..9bcaf3f6 --- /dev/null +++ b/testsuite/buildok/null.stp @@ -0,0 +1,5 @@ +#! stap -p4 + +# PR10298 +global foo = 5 +probe begin { if (foo == NULL) log("hello") } diff --git a/testsuite/systemtap.base/static_uprobes.exp b/testsuite/systemtap.base/sdt_misc.exp index 6a597646..459b669a 100644 --- a/testsuite/systemtap.base/static_uprobes.exp +++ b/testsuite/systemtap.base/sdt_misc.exp @@ -33,9 +33,15 @@ baz (int i, char* s) void buz (int parm) { + struct astruct + { + int a; + int b; + }; + struct astruct bstruct = {parm, parm + 1}; if (parm == 0) parm = 1000; - DTRACE_PROBE1(static_user_markers,test_probe_4,parm); + DTRACE_PROBE1(static_user_markers,test_probe_4,&bstruct); } int @@ -65,21 +71,23 @@ probe process(\"static_user_markers.x\").mark(\"test_probe_3\") } probe process(\"static_user_markers.x\").mark(\"test_probe_4\") { - printf(\"In test_probe_4 dtrace probe %#x\\n\", \$arg1) + printf(\"In test_probe_4 dtrace probe %#x %#x\\n\", \$arg1->a, \$arg1->b) } " close $fp set sup_dpath "[pwd]/static_user_markers_.d" set sup_hpath "[pwd]/static_user_markers_.h" +set sup_opath "[pwd]/static_user_markers_.o" set fp [open $sup_dpath "w"] puts $fp " provider static_user_markers { probe test_probe_1 (); probe test_probe_2 (int i); probe test_probe_3 (int i, char* x); - probe test_probe_4 (int i); + probe test_probe_4 (struct astruct arg); }; +struct astruct {int a; int b;}; " close $fp @@ -90,10 +98,10 @@ if {[installtest_p]} { } else { set dtrace $srcdir/../dtrace } -if {[catch {exec $dtrace -h -s $sup_dpath} res]} { +if {[catch {exec $dtrace --types -G -s $sup_dpath} res]} { verbose -log "unable to run $dtrace: $res" } -if {[file exists $sup_hpath]} then { +if {[file exists $sup_hpath] && [file exists $sup_opath]} then { pass "$test dtrace" } else { fail "$test dtrace" @@ -109,7 +117,7 @@ if {[installtest_p]} { set sdtdir $srcdir/../includes } -set pbtype_flags {{""} {additional_flags=-DEXPERIMENTAL_UTRACE_SDT} {additional_flags=-DEXPERIMENTAL_KPROBE_SDT}} +set pbtype_flags {{""} {additional_flags=-O additional_flags=-DEXPERIMENTAL_UTRACE_SDT} {additional_flags=-O additional_flags=-DEXPERIMENTAL_KPROBE_SDT}} set pbtype_mssgs {{uprobe} {utrace} {kprobe}} # Iterate pbtype_flags @@ -121,8 +129,8 @@ set testprog "sdt.c.exe.$i" set sup_flags "additional_flags=-I$srcdir/../includes/sys" set sup_flags "$sup_flags additional_flags=-I$sdtdir" set sup_flags "$sup_flags additional_flags=-g" -set sup_flags "$sup_flags additional_flags=-O" -set sup_flags "$sup_flags additional_flags=-I. $pbtype_flag" +set sup_flags "$sup_flags additional_flags=$sup_opath" +set sup_flags "$sup_flags additional_flags=-I. $pbtype_flag" set res [target_compile $sup_srcpath $sup_exepath executable $sup_flags] if { $res != "" } { verbose "target_compile failed: $res" 2 @@ -155,7 +163,7 @@ expect { -re {In test_probe_2 probe 0x2} { incr ok; exp_continue } -re {In test_probe_0 probe 0x3} { incr ok; exp_continue } -re {In test_probe_3 probe 0x3 0x[0-9a-f][0-9a-f]} { incr ok; exp_continue } - -re {In test_probe_4 dtrace probe 0x4} { incr ok; exp_continue } + -re {In test_probe_4 dtrace probe 0x4 0x5} { incr ok; exp_continue } timeout { fail "$test (timeout)" } eof { } } @@ -170,7 +178,7 @@ if {$ok == 5} { # Test passing various C types to .mark probes -set sup_flags "$sup_flags additional_flags=-O0 $pbtype_flag" +set sup_flags "$sup_flags $pbtype_flag additional_flags=-O0 " set res [target_compile $srcdir/$subdir/sdt_types.c sdt_types.x executable $sup_flags] if { $res != "" } { verbose "target_compile failed: $res" 2 diff --git a/testsuite/systemtap.examples/index.html b/testsuite/systemtap.examples/index.html index b2ed3a3a..c485713c 100644 --- a/testsuite/systemtap.examples/index.html +++ b/testsuite/systemtap.examples/index.html @@ -97,6 +97,9 @@ keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-inde <li><a href="network/nettop.stp">network/nettop.stp</a> - Periodic Listing of Processes Using Network Interfaces<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TRAFFIC">TRAFFIC</a> <a href="keyword-index.html#PER-PROCESS">PER-PROCESS</a> <br> <p>Every five seconds the nettop.stp script prints out a list of processed (PID and command) with the number of packets sent/received and the amount of data sent/received by the process during that interval.</p></li> +<li><a href="network/sk_stream_wait_memory.stp">network/sk_stream_wait_memory.stp</a> - Track Start and Stop of Processes Due to Network Buffer Space<br> +keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#MEMORY">MEMORY</a> <br> +<p>The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer.</p></li> <li><a href="network/socket-trace.stp">network/socket-trace.stp</a> - Trace Functions called in Network Socket Code<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#SOCKET">SOCKET</a> <br> <p>The script instrument each of the functions inn the Linux kernel's net/socket.c file. The script prints out trace. The first element of a line is time delta in microseconds from the previous entry. This is followed by the command name and the PID. The "->" and "<-" indicates function entry and function exit, respectively. The last element of the line is the function name.</p></li> @@ -109,6 +112,9 @@ keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-inde <li><a href="process/errsnoop.stp">process/errsnoop.stp</a> - tabulate system call errors<br> keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SYSCALL">SYSCALL</a> <br> <p>The script prints a periodic tabular report about failing system calls, by process and by syscall failure. The first optional argument specifies the reporting interval (in seconds, default 5); the second optional argument gives a screen height (number of lines in the report, default 20).</p></li> +<li><a href="process/forktracker.stp">process/forktracker.stp</a> - Trace Creation of Processes<br> +keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SCHEDULER">SCHEDULER</a> <br> +<p>The forktracker.stp script prints out a time-stamped entry showing each fork and exec operation on the machine. This can be useful for determine what process is creating a flurry of short-lived processes.</p></li> <li><a href="process/futexes.stp">process/futexes.stp</a> - System-Wide Futex Contention<br> keywords: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <a href="keyword-index.html#LOCKING">LOCKING</a> <a href="keyword-index.html#FUTEX">FUTEX</a> <br> <p>The script watches the futex syscall on the system. On exit the futexes address, the number of contentions, and the average time for each contention on the futex are printed from lowest pid number to highest.</p></li> diff --git a/testsuite/systemtap.examples/index.txt b/testsuite/systemtap.examples/index.txt index 91fc66ae..caf1a5ff 100644 --- a/testsuite/systemtap.examples/index.txt +++ b/testsuite/systemtap.examples/index.txt @@ -176,6 +176,15 @@ keywords: network traffic per-process interval. +network/sk_stream_wait_memory.stp - Track Start and Stop of Processes Due to Network Buffer Space +keywords: network tcp buffer memory + + The sk_stream-wait_memory.stp prints a time stamp, executable, and + pid each time a process blocks due to the send buffer being full. A + similar entry is printed each time a process continues because there + is room in the buffer. + + network/socket-trace.stp - Trace Functions called in Network Socket Code keywords: network socket @@ -215,6 +224,14 @@ keywords: process syscall in the report, default 20). +process/forktracker.stp - Trace Creation of Processes +keywords: process scheduler + + The forktracker.stp script prints out a time-stamped entry showing + each fork and exec operation on the machine. This can be useful for + determine what process is creating a flurry of short-lived processes. + + process/futexes.stp - System-Wide Futex Contention keywords: syscall locking futex diff --git a/testsuite/systemtap.examples/keyword-index.html b/testsuite/systemtap.examples/keyword-index.html index 7306c164..9852c992 100644 --- a/testsuite/systemtap.examples/keyword-index.html +++ b/testsuite/systemtap.examples/keyword-index.html @@ -57,6 +57,9 @@ keywords: <a href="keyword-index.html#IO">IO</a> <a href="keyword-index.html#SCH <li><a href="network/dropwatch.stp">network/dropwatch.stp</a> - Watch Where Socket Buffers are Freed in the Kernel<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TRACEPOINT">TRACEPOINT</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#FREE">FREE</a> <br> <p>Every five seconds the dropwatch.stp script lists the number of socket buffers freed at locations in the kernel.</p></li> +<li><a href="network/sk_stream_wait_memory.stp">network/sk_stream_wait_memory.stp</a> - Track Start and Stop of Processes Due to Network Buffer Space<br> +keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#MEMORY">MEMORY</a> <br> +<p>The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer.</p></li> </ul> <h3><a name="CALLGRAPH">CALLGRAPH</a></h3> <ul> @@ -162,6 +165,9 @@ keywords: <a href="keyword-index.html#MEMORY">MEMORY</a> <br> <li><a href="memory/pfaults.stp">memory/pfaults.stp</a> - Generate Log of Major and Minor Page Faults<br> keywords: <a href="keyword-index.html#MEMORY">MEMORY</a> <br> <p>The pfaults.stp script generates a simple log for each major and minor page fault that occurs on the system. Each line contains a timestamp (in microseconds) when the page fault servicing was completed, the pid of the process, the address of the page fault, the type of access (read or write), the type of fault (major or minor), and the elapsed time for page fault. This log can be examined to determine where the page faults are occuring.</p></li> +<li><a href="network/sk_stream_wait_memory.stp">network/sk_stream_wait_memory.stp</a> - Track Start and Stop of Processes Due to Network Buffer Space<br> +keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#MEMORY">MEMORY</a> <br> +<p>The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer.</p></li> </ul> <h3><a name="MONITOR">MONITOR</a></h3> <ul> @@ -177,6 +183,9 @@ keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-inde <li><a href="network/nettop.stp">network/nettop.stp</a> - Periodic Listing of Processes Using Network Interfaces<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TRAFFIC">TRAFFIC</a> <a href="keyword-index.html#PER-PROCESS">PER-PROCESS</a> <br> <p>Every five seconds the nettop.stp script prints out a list of processed (PID and command) with the number of packets sent/received and the amount of data sent/received by the process during that interval.</p></li> +<li><a href="network/sk_stream_wait_memory.stp">network/sk_stream_wait_memory.stp</a> - Track Start and Stop of Processes Due to Network Buffer Space<br> +keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#MEMORY">MEMORY</a> <br> +<p>The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer.</p></li> <li><a href="network/socket-trace.stp">network/socket-trace.stp</a> - Trace Functions called in Network Socket Code<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#SOCKET">SOCKET</a> <br> <p>The script instrument each of the functions inn the Linux kernel's net/socket.c file. The script prints out trace. The first element of a line is time delta in microseconds from the previous entry. This is followed by the command name and the PID. The "->" and "<-" indicates function entry and function exit, respectively. The last element of the line is the function name.</p></li> @@ -201,6 +210,9 @@ keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-inde <li><a href="process/errsnoop.stp">process/errsnoop.stp</a> - tabulate system call errors<br> keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SYSCALL">SYSCALL</a> <br> <p>The script prints a periodic tabular report about failing system calls, by process and by syscall failure. The first optional argument specifies the reporting interval (in seconds, default 5); the second optional argument gives a screen height (number of lines in the report, default 20).</p></li> +<li><a href="process/forktracker.stp">process/forktracker.stp</a> - Trace Creation of Processes<br> +keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SCHEDULER">SCHEDULER</a> <br> +<p>The forktracker.stp script prints out a time-stamped entry showing each fork and exec operation on the machine. This can be useful for determine what process is creating a flurry of short-lived processes.</p></li> <li><a href="process/schedtimes.stp">process/schedtimes.stp</a> - Track Time Processes Spend in Various States using Tracepoints<br> keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SCHEDULER">SCHEDULER</a> <a href="keyword-index.html#TIME">TIME</a> <a href="keyword-index.html#TRACEPOINT">TRACEPOINT</a> <br> <p>The schedtimes.stp script instruments the scheduler to track the amount of time that each process spends running, sleeping, queued, and waiting for io. On exit the script prints out the accumulated time for each state of processes observed. Optionally, this script can be used with the '-c' or '-x' options to focus on a specific PID.</p></li> @@ -234,6 +246,9 @@ keywords: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <a href="keyword-inde </ul> <h3><a name="SCHEDULER">SCHEDULER</a></h3> <ul> +<li><a href="process/forktracker.stp">process/forktracker.stp</a> - Trace Creation of Processes<br> +keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SCHEDULER">SCHEDULER</a> <br> +<p>The forktracker.stp script prints out a time-stamped entry showing each fork and exec operation on the machine. This can be useful for determine what process is creating a flurry of short-lived processes.</p></li> <li><a href="process/schedtimes.stp">process/schedtimes.stp</a> - Track Time Processes Spend in Various States using Tracepoints<br> keywords: <a href="keyword-index.html#PROCESS">PROCESS</a> <a href="keyword-index.html#SCHEDULER">SCHEDULER</a> <a href="keyword-index.html#TIME">TIME</a> <a href="keyword-index.html#TRACEPOINT">TRACEPOINT</a> <br> <p>The schedtimes.stp script instruments the scheduler to track the amount of time that each process spends running, sleeping, queued, and waiting for io. On exit the script prints out the accumulated time for each state of processes observed. Optionally, this script can be used with the '-c' or '-x' options to focus on a specific PID.</p></li> @@ -303,6 +318,9 @@ keywords: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <a href="keyword-inde </ul> <h3><a name="TCP">TCP</a></h3> <ul> +<li><a href="network/sk_stream_wait_memory.stp">network/sk_stream_wait_memory.stp</a> - Track Start and Stop of Processes Due to Network Buffer Space<br> +keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#BUFFER">BUFFER</a> <a href="keyword-index.html#MEMORY">MEMORY</a> <br> +<p>The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer.</p></li> <li><a href="network/tcp_connections.stp">network/tcp_connections.stp</a> - Track Creation of Incoming TCP Connections<br> keywords: <a href="keyword-index.html#NETWORK">NETWORK</a> <a href="keyword-index.html#TCP">TCP</a> <a href="keyword-index.html#SOCKET">SOCKET</a> <br> <p>The tcp_connections.stp script prints information for each new incoming TCP connection accepted by the computer. The information includes the UID, the command accepting the connection, the PID of the command, the port the connection is on, and the IP address of the originator of the request.</p></li> diff --git a/testsuite/systemtap.examples/keyword-index.txt b/testsuite/systemtap.examples/keyword-index.txt index eee89e22..4778afc7 100644 --- a/testsuite/systemtap.examples/keyword-index.txt +++ b/testsuite/systemtap.examples/keyword-index.txt @@ -39,6 +39,15 @@ keywords: network tracepoint buffer free socket buffers freed at locations in the kernel. +network/sk_stream_wait_memory.stp - Track Start and Stop of Processes Due to Network Buffer Space +keywords: network tcp buffer memory + + The sk_stream-wait_memory.stp prints a time stamp, executable, and + pid each time a process blocks due to the send buffer being full. A + similar entry is printed each time a process continues because there + is room in the buffer. + + = CALLGRAPH = general/para-callgraph.stp - Callgraph tracing with arguments @@ -275,6 +284,15 @@ keywords: memory determine where the page faults are occuring. +network/sk_stream_wait_memory.stp - Track Start and Stop of Processes Due to Network Buffer Space +keywords: network tcp buffer memory + + The sk_stream-wait_memory.stp prints a time stamp, executable, and + pid each time a process blocks due to the send buffer being full. A + similar entry is printed each time a process continues because there + is room in the buffer. + + = MONITOR = io/ttyspy.stp - Monitor tty typing. @@ -303,6 +321,15 @@ keywords: network traffic per-process interval. +network/sk_stream_wait_memory.stp - Track Start and Stop of Processes Due to Network Buffer Space +keywords: network tcp buffer memory + + The sk_stream-wait_memory.stp prints a time stamp, executable, and + pid each time a process blocks due to the send buffer being full. A + similar entry is printed each time a process continues because there + is room in the buffer. + + network/socket-trace.stp - Trace Functions called in Network Socket Code keywords: network socket @@ -363,6 +390,14 @@ keywords: process syscall in the report, default 20). +process/forktracker.stp - Trace Creation of Processes +keywords: process scheduler + + The forktracker.stp script prints out a time-stamped entry showing + each fork and exec operation on the machine. This can be useful for + determine what process is creating a flurry of short-lived processes. + + process/schedtimes.stp - Track Time Processes Spend in Various States using Tracepoints keywords: process scheduler time tracepoint @@ -453,6 +488,14 @@ keywords: syscall read write time io = SCHEDULER = +process/forktracker.stp - Trace Creation of Processes +keywords: process scheduler + + The forktracker.stp script prints out a time-stamped entry showing + each fork and exec operation on the machine. This can be useful for + determine what process is creating a flurry of short-lived processes. + + process/schedtimes.stp - Track Time Processes Spend in Various States using Tracepoints keywords: process scheduler time tracepoint @@ -624,6 +667,15 @@ keywords: syscall wait4 = TCP = +network/sk_stream_wait_memory.stp - Track Start and Stop of Processes Due to Network Buffer Space +keywords: network tcp buffer memory + + The sk_stream-wait_memory.stp prints a time stamp, executable, and + pid each time a process blocks due to the send buffer being full. A + similar entry is printed each time a process continues because there + is room in the buffer. + + network/tcp_connections.stp - Track Creation of Incoming TCP Connections keywords: network tcp socket diff --git a/testsuite/systemtap.examples/network/sk_stream_wait_memory.meta b/testsuite/systemtap.examples/network/sk_stream_wait_memory.meta new file mode 100644 index 00000000..bc798f72 --- /dev/null +++ b/testsuite/systemtap.examples/network/sk_stream_wait_memory.meta @@ -0,0 +1,13 @@ +title: Track Start and Stop of Processes Due to Network Buffer Space +name: sk_stream_wait_memory.stp +version: 1.0 +author: Fabio Olive Leite at Red Hat +keywords: network tcp buffer memory +subsystem: kernel +status: production +exit: user-controlled +output: trace +scope: system-wide +description: The sk_stream-wait_memory.stp prints a time stamp, executable, and pid each time a process blocks due to the send buffer being full. A similar entry is printed each time a process continues because there is room in the buffer. +test_check: stap -p4 sk_stream_wait_memory.stp +test_installcheck: stap sk_stream_wait_memory.stp -c "sleep 1" diff --git a/testsuite/systemtap.examples/network/sk_stream_wait_memory.stp b/testsuite/systemtap.examples/network/sk_stream_wait_memory.stp new file mode 100755 index 00000000..159d77a6 --- /dev/null +++ b/testsuite/systemtap.examples/network/sk_stream_wait_memory.stp @@ -0,0 +1,25 @@ +# Simple probe to detect when a process is waiting for more socket send +# buffer memory. Usually means the process is doing writes larger than the +# socker send buffer size or there is a slow receiver at the other side. +# Increasing the socket's send buffer size might help decrease application +# latencies, but it might also make it worse, so buyer beware. +# +# Tipical output: timestamp in microseconds: procname(pid) event +# +# 1218230114875167: python(17631) blocked on full send buffer +# 1218230114876196: python(17631) recovered from full send buffer +# 1218230114876271: python(17631) blocked on full send buffer +# 1218230114876479: python(17631) recovered from full send buffer + +probe kernel.function("sk_stream_wait_memory") +{ + printf("%u: %s(%d) blocked on full send buffer\n", + gettimeofday_us(), execname(), pid()) +} + +probe kernel.function("sk_stream_wait_memory").return +{ + printf("%u: %s(%d) recovered from full send buffer\n", + gettimeofday_us(), execname(), pid()) +} + diff --git a/testsuite/systemtap.examples/process/forktracker.meta b/testsuite/systemtap.examples/process/forktracker.meta new file mode 100644 index 00000000..f3ef3709 --- /dev/null +++ b/testsuite/systemtap.examples/process/forktracker.meta @@ -0,0 +1,13 @@ +title: Trace Creation of Processes +name: forktracker.stp +version: 0.1 +author: Phil Muldoon at Red Hat +keywords: process scheduler +subsystem: scheduler +status: production +exit: user-controlled +output: trace +scope: system-wide +description: The forktracker.stp script prints out a time-stamped entry showing each fork and exec operation on the machine. This can be useful for determine what process is creating a flurry of short-lived processes. +test_check: stap -p4 forktracker.stp +test_installcheck: stap forkracker.stp -c "sleep 1" diff --git a/testsuite/systemtap.examples/process/forktracker.stp b/testsuite/systemtap.examples/process/forktracker.stp new file mode 100755 index 00000000..525aa0a5 --- /dev/null +++ b/testsuite/systemtap.examples/process/forktracker.stp @@ -0,0 +1,23 @@ +#! /usr/bin/env stap +# +# This is a stap script to monitor process creations (fork(), exec()'s). +# Based off of stap script found: http://picobot.org/wordpress/?p=27 +# With some minor modifications (i.e. timestamping) +# +# Usage: stap forktracker.stp +# +# Sample output: +# +# timestamp: process_name (pid) new_pid +# +# Version 0.1 + +probe kprocess.create { + printf("%-25s: %s (%d) created %d\n", + ctime(gettimeofday_s()), execname(), pid(), new_pid) +} + +probe kprocess.exec { + printf("%-25s: %s (%d) is exec'ing %s\n", + ctime(gettimeofday_s()), execname(), pid(), filename) +} diff --git a/testsuite/systemtap.printf/end1.exp b/testsuite/systemtap.printf/end1.exp index 590340d3..ab1de590 100644 --- a/testsuite/systemtap.printf/end1.exp +++ b/testsuite/systemtap.printf/end1.exp @@ -11,15 +11,15 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME - puts "$res" + fail $TEST_NAME + puts "stap failed: $res" catch {exec rm -f $tmpfile} return } if {[catch {exec cmp $tmpfile $srcdir/$subdir/large_output} res]} { - fail $TEST_NAME puts "$res" + fail $TEST_NAME catch {exec rm -f $tmpfile} return } diff --git a/testsuite/systemtap.printf/end1b.exp b/testsuite/systemtap.printf/end1b.exp index bea5736e..46cdc9c7 100644 --- a/testsuite/systemtap.printf/end1b.exp +++ b/testsuite/systemtap.printf/end1b.exp @@ -16,8 +16,8 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME puts "stap failed: $res" + fail $TEST_NAME as_root "/bin/rm -f [glob ${tmpfile}*]" return } diff --git a/testsuite/systemtap.printf/mixed_out.exp b/testsuite/systemtap.printf/mixed_out.exp index 3b66e7c0..55320e80 100644 --- a/testsuite/systemtap.printf/mixed_out.exp +++ b/testsuite/systemtap.printf/mixed_out.exp @@ -11,8 +11,8 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -o $tmpfile $tpath} res]} { - untested $TEST_NAME - puts "$res" + fail $TEST_NAME + puts "stap failed: $res" catch {exec rm -f $tmpfile} return } diff --git a/testsuite/systemtap.printf/mixed_outb.exp b/testsuite/systemtap.printf/mixed_outb.exp index db82cc79..c15520b1 100644 --- a/testsuite/systemtap.printf/mixed_outb.exp +++ b/testsuite/systemtap.printf/mixed_outb.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out1.exp b/testsuite/systemtap.printf/out1.exp index 7577a54d..f973ae00 100644 --- a/testsuite/systemtap.printf/out1.exp +++ b/testsuite/systemtap.printf/out1.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out1b.exp b/testsuite/systemtap.printf/out1b.exp index c3e21ba9..24efbf4c 100644 --- a/testsuite/systemtap.printf/out1b.exp +++ b/testsuite/systemtap.printf/out1b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out2.exp b/testsuite/systemtap.printf/out2.exp index ce1f7c6e..8c66e73d 100644 --- a/testsuite/systemtap.printf/out2.exp +++ b/testsuite/systemtap.printf/out2.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out2b.exp b/testsuite/systemtap.printf/out2b.exp index fcc12f63..70a98ea2 100644 --- a/testsuite/systemtap.printf/out2b.exp +++ b/testsuite/systemtap.printf/out2b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/testsuite/systemtap.printf/out3.exp b/testsuite/systemtap.printf/out3.exp index 51124757..63a67d8f 100644 --- a/testsuite/systemtap.printf/out3.exp +++ b/testsuite/systemtap.printf/out3.exp @@ -11,7 +11,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -o $tmpfile $tpath} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "$res" catch {exec rm -f $tmpfile} return diff --git a/testsuite/systemtap.printf/out3b.exp b/testsuite/systemtap.printf/out3b.exp index 740a8b68..d49625e9 100644 --- a/testsuite/systemtap.printf/out3b.exp +++ b/testsuite/systemtap.printf/out3b.exp @@ -16,7 +16,7 @@ if {[catch {exec mktemp -t staptestXXXXXX} tmpfile]} { } if {[catch {exec stap -DMAXACTION=100000 -b -o $tmpfile $test} res]} { - untested $TEST_NAME + fail $TEST_NAME puts "stap failed: $res" as_root "/bin/rm -f [glob ${tmpfile}*]" return diff --git a/translate.cxx b/translate.cxx index d9d99507..5336dc66 100644 --- a/translate.cxx +++ b/translate.cxx @@ -5212,18 +5212,19 @@ translate_pass (systemtap_session& s) s.op->newline(-1) << "}"; s.op->assert_0_indent(); + emit_symbol_data (s); + + s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");"; + s.op->newline() << "MODULE_LICENSE(\"GPL\");"; + s.op->assert_0_indent(); + + // PR10298: attempt to avoid collisions with symbols for (unsigned i=0; i<s.globals.size(); i++) { s.op->newline(); s.up->emit_global_param (s.globals[i]); } s.op->assert_0_indent(); - - emit_symbol_data (s); - - s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");"; - s.op->newline() << "MODULE_LICENSE(\"GPL\");"; - s.op->assert_0_indent(); } catch (const semantic_error& e) { |