commit 44e00f1b84b835c908895846267177d0f19e3f5e Author: CentOS Sources Date: Sat Feb 13 06:13:44 2021 +0000 import systemtap-4.4-9.el8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b8f7ea7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/systemtap-4.4.tar.gz diff --git a/.systemtap.metadata b/.systemtap.metadata new file mode 100644 index 0000000..accf03e --- /dev/null +++ b/.systemtap.metadata @@ -0,0 +1 @@ +f126888adda90a0ec57f43f9db20fde68c8ef356 SOURCES/systemtap-4.4.tar.gz diff --git a/SOURCES/rhbz1650594.patch b/SOURCES/rhbz1650594.patch new file mode 100644 index 0000000..fd5afa9 --- /dev/null +++ b/SOURCES/rhbz1650594.patch @@ -0,0 +1,55 @@ +commit 734f5acf6c568c02254a33730c6f0fff016bdd09 +Author: Martin Cermak +Date: Wed Jan 20 22:09:49 2021 +0100 + + systemtap-service onboot: Skip updating the bootloader + + It shows up that just modifying the default initrd is good enough, + no need to call kernel-install or new-kernel-pkg. This speeds up + the systemtap-service onboot operation. + +diff --git a/initscript/systemtap.in b/initscript/systemtap.in +index 713f7a680..b1621ae2f 100755 +--- a/initscript/systemtap.in ++++ b/initscript/systemtap.in +@@ -46,8 +46,6 @@ STAPRUN=@bindir@/staprun + UNAME=/bin/uname + LSMOD=/sbin/lsmod + DRACUT=`which dracut` +-NEWKERNELPKG=/sbin/new-kernel-pkg +-KERNELINSTALL=/usr/bin/kernel-install + + # Not actually used directly, but needed by + # stap dracut module for inclusion in initramfs +@@ -839,10 +837,6 @@ backup_initramfs() { + + onboot () { + local s ret ss +- if [ ! -f "$NEWKERNELPKG" -a ! -f "$KERNELINSTALL" ]; then +- do_failure "Could not find $NEWKERNELPKG nor $KERNELINSTALL" +- return 1 +- fi + if [ ! -f "$DRACUT" ]; then + do_failure "$DRACUT not found" + return 1 +@@ -940,20 +934,6 @@ onboot () { + return 0 + fi + clog "done" +- # We're installing the initramfs in the default location, so user +- # expects the next boot to use it. Let's also update the bootloader. +- clog " Updating bootloader ... " -n +- if [ -x "$NEWKERNELPKG" ]; then +- logex $NEWKERNELPKG --initrdfile="$INITRAMFS" \ +- --update $KRELEASE +- else +- logex $KERNELINSTALL add $KRELEASE /boot/vmlinuz-$KRELEASE +- fi +- if [ $? -ne 0 ]; then +- do_failure "bootloader update exited with nonzero status" +- return 1 +- fi +- might_success "initramfs created and bootloader updated" + return 0 + } + diff --git a/SOURCES/rhbz1873492.patch b/SOURCES/rhbz1873492.patch new file mode 100644 index 0000000..f843fbf --- /dev/null +++ b/SOURCES/rhbz1873492.patch @@ -0,0 +1,108 @@ +commit ea5f10ba55fce68d1ed614ca33afdb38816f0830 +Author: Frank Ch. Eigler +Date: Mon Nov 16 18:54:11 2020 -0500 + + PR26665: mokutil output parsing tweaks + + We encountered secureboot keys in the wild that didn't live up + to the expectations of the current little state machine. Tweaked + regexps to accept Issuer: O= as well as Issuer: CN= lines. With + more verbosity, produces output on parsing process. + +diff --git a/session.cxx b/session.cxx +index b5a8044..0437ca4 100644 +--- a/session.cxx ++++ b/session.cxx +@@ -2859,6 +2859,9 @@ systemtap_session::get_mok_info() + // PR26665: but only Systemtap MOK keys; there may be others. + getline(out, line); + ++ if (verbose > 3) ++ clog << "MOK parse state: " << state << " line: " << line << endl; ++ + if (state == "SHA1") { // look for a new key fingerprint + if (! regexp_match(line, "^SHA1 Fingerprint: ([0-9a-f:]+)$", matches)) + { +@@ -2871,11 +2874,14 @@ systemtap_session::get_mok_info() + } + // else stay in SHA1 state + } else if (state == "Issuer") { // validate issuer +- if (! regexp_match(line, "^[ \t]*Issuer: O=(.*)$", matches)) { ++ if (! regexp_match(line, "^[ \t]*Issuer: [A-Z]*=(.*)$", matches)) { + if (verbose > 2) + clog << "Issuer found: " << matches[1] << endl; +- if (! regexp_match(matches[1], "Systemtap", matches)) ++ if (! regexp_match(matches[1], "Systemtap", matches)) { ++ if (verbose > 2) ++ clog << "Recognized Systemtap MOK fingerprint: " << fingerprint << endl; + mok_fingerprints.push_back(fingerprint); ++ } + state = "SHA1"; // start looking for another key + } + } else { // some other line in mokutil output ... there are plenty +commit 532eb9a1502026300a7f0b4bd287499101dd5803 +Author: Frank Ch. Eigler +Date: Tue Nov 17 16:34:59 2020 -0500 + + PR26665 detect rhel8 (4.18) era kernel_is_locked_down() as procfs trigger + + A different older kernel API needs to be probed for rhel8 era detection + of lockdown in effect. Added an (undocumented) $SYSTEMTAP_NOSIGN env + var to override automatic --use-server on lockdown, so that one can + inspect runtime/autoconf* operation locally, without stap-server. + +diff --git a/buildrun.cxx b/buildrun.cxx +index 9b4066d..9c8e648 100644 +--- a/buildrun.cxx ++++ b/buildrun.cxx +@@ -517,6 +517,7 @@ compile_pass (systemtap_session& s) + output_autoconf(s, o, cs, "autoconf-atomic_fetch_add_unless.c", + "STAPCONF_ATOMIC_FETCH_ADD_UNLESS", NULL); + output_autoconf(s, o, cs, "autoconf-lockdown-debugfs.c", "STAPCONF_LOCKDOWN_DEBUGFS", NULL); ++ output_autoconf(s, o, cs, "autoconf-lockdown-kernel.c", "STAPCONF_LOCKDOWN_KERNEL", NULL); + + // used by runtime/linux/netfilter.c + output_exportconf(s, o2, "nf_register_hook", "STAPCONF_NF_REGISTER_HOOK"); +diff --git a/runtime/linux/autoconf-lockdown-kernel.c b/runtime/linux/autoconf-lockdown-kernel.c +new file mode 100644 +index 0000000..90c2414 +--- /dev/null ++++ b/runtime/linux/autoconf-lockdown-kernel.c +@@ -0,0 +1,5 @@ ++#include ++ ++int foo(void) { ++ return kernel_is_locked_down("something"); ++} +diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c +index bb4a98b..5795533 100644 +--- a/runtime/transport/transport.c ++++ b/runtime/transport/transport.c +@@ -123,6 +123,12 @@ static int _stp_transport_fs_init(const char *module_name) + dbug_trans(1, "choosing procfs_p=1\n"); + } + #endif ++#ifdef STAPCONF_LOCKDOWN_KERNEL ++ if (!debugfs_p && kernel_is_locked_down ("debugfs")) { ++ procfs_p = 1; ++ dbug_trans(1, "choosing procfs_p=1\n"); ++ } ++#endif + if (!procfs_p) { + debugfs_p = 1; + dbug_trans(1, "choosing debugfs_p=1\n"); +diff --git a/session.cxx b/session.cxx +index 0437ca4..36a4053 100644 +--- a/session.cxx ++++ b/session.cxx +@@ -2804,7 +2804,9 @@ systemtap_session::modules_must_be_signed() + + if (getenv("SYSTEMTAP_SIGN")) + return true; +- ++ if (getenv("SYSTEMTAP_NOSIGN")) ++ return false; ++ + statm >> status; + if (status == 'Y') + return true; diff --git a/SOURCES/rhbz1898288.patch b/SOURCES/rhbz1898288.patch new file mode 100644 index 0000000..c3f1653 --- /dev/null +++ b/SOURCES/rhbz1898288.patch @@ -0,0 +1,63 @@ +commit 34e62f15da5adf06361ac66489936d0ffa1cc430 +Author: Frank Ch. Eigler +Date: Tue Nov 10 22:13:53 2020 -0500 + + RHBZ1892179: handle exhausted stp_task_work structs + + In utrace_report_syscall_entry and _exit, there is a possibility of + dereferencing a NULL pointer, in case __stp_utrace_alloc_task_work + exhausts UTRACE_TASK_WORK_POOL_SIZE live elements. While OOM is + still a possibility, this patch handles it more gracefully. + +diff --git a/runtime/stp_utrace.c b/runtime/stp_utrace.c +index 47355de..e2880f1 100644 +--- a/runtime/stp_utrace.c ++++ b/runtime/stp_utrace.c +@@ -2337,11 +2337,11 @@ static void utrace_report_syscall_entry(void *cb_data __attribute__ ((unused)), + + /* Defer the report_syscall_entry work so it doesn't happen in atomic context: */ + work = __stp_utrace_alloc_task_work(utrace, NULL); +- __stp_utrace_save_regs(work, regs); + if (work == NULL) { + _stp_error("Unable to allocate space for task_work"); + return; + } ++ __stp_utrace_save_regs(work, regs); + stp_init_task_work(work, &utrace_syscall_entry_work); + rc = stp_task_work_add(task, work); + // stp_task_work_add() returns -ESRCH if the task has already +@@ -2444,11 +2444,11 @@ static void utrace_report_syscall_exit(void *cb_data __attribute__ ((unused)), + + /* Defer the report_syscall_exit work so it doesn't happen in atomic context: */ + work = __stp_utrace_alloc_task_work(utrace, NULL); +- __stp_utrace_save_regs(work, regs); + if (work == NULL) { + _stp_error("Unable to allocate space for task_work"); + return; + } ++ __stp_utrace_save_regs(work, regs); + stp_init_task_work(work, &utrace_syscall_exit_work); + rc = stp_task_work_add(task, work); + // stp_task_work_add() returns -ESRCH if the task has already + +commit 83cb271b390a1b36abd4c3aa69f89c466e99e253 +Author: Frank Ch. Eigler +Date: Fri Nov 13 12:36:07 2020 -0500 + + RHBZ1892179: double default UTRACE_TASK_WORKPOOL + + Some workloads were observed to exhaust the previous limit of 288. + +diff --git a/runtime/stp_utrace.c b/runtime/stp_utrace.c +index 46ba489..6022267 100644 +--- a/runtime/stp_utrace.c ++++ b/runtime/stp_utrace.c +@@ -141,7 +141,7 @@ struct __stp_utrace_task_work { /* NB: about 216 bytes, 18 per page: */ + TODO: UTRACE_TASK_WORK_POOL_SIZE can be specified on the Systemtap + command line. Experiment to find the best default value. */ + #ifndef UTRACE_TASK_WORK_POOL_SIZE +-#define UTRACE_TASK_WORK_POOL_SIZE 288 ++#define UTRACE_TASK_WORK_POOL_SIZE 576 + #endif + static DECLARE_BITMAP(__stp_utrace_task_work_pool_bitmap, UTRACE_TASK_WORK_POOL_SIZE); + static struct __stp_utrace_task_work __stp_utrace_task_work_pool[UTRACE_TASK_WORK_POOL_SIZE]; diff --git a/SOURCES/rhbz1902696.patch b/SOURCES/rhbz1902696.patch new file mode 100644 index 0000000..71d3c0f --- /dev/null +++ b/SOURCES/rhbz1902696.patch @@ -0,0 +1,217 @@ +commit e3d03db82853049f65f16dc40c03f3f7f617ffb5 +Author: Frank Ch. Eigler +Date: Sun Dec 13 21:05:23 2020 -0500 + + PR23512: fix staprun/stapio operation via less-than-root privileges + + Commit 7615cae790c899bc8a82841c75c8ea9c6fa54df3 for PR26665 introduced + a regression in handling stapusr/stapdev/stapsys gid invocation of + staprun/stapio. This patch simplifies the relevant code in + staprun/ctl.c, init_ctl_channel(), to rely on openat/etc. to populate + and use the relay_basedir_fd as much as possible. Also, we now avoid + unnecessary use of access(), which was checking against the wrong + (real rather than effective) uid/gid. + +diff --git a/staprun/ctl.c b/staprun/ctl.c +index 4be68af..da3417b 100644 +--- a/staprun/ctl.c ++++ b/staprun/ctl.c +@@ -14,111 +14,70 @@ + + #define CTL_CHANNEL_NAME ".cmd" + ++ ++#ifndef HAVE_OPENAT ++#error "need openat" ++#endif ++ ++ ++// This function does multiple things: ++// ++// 1) if needed, open the running module's directory (the one that ++// contains .ctl), stash fd in relay_basedir_fd; this will be ++// passed to stapio children via -F$fd for privilege passing ++// ++// 2) (re)open the running module's .ctl file, stash fd in the ++// control_channel global; this will be used all over the place. ++// ++// Return 0 on success. ++// ++// See also PR14245, PR26665, RHBZ1902696 = PR23512 ++// + int init_ctl_channel(const char *name, int verb) + { +- char buf[PATH_MAX] = ""; // the .ctl file name +- char buf2[PATH_MAX] = ""; // other tmp stuff +- struct statfs st; +- + (void) verb; +- if (0) goto out; /* just to defeat gcc warnings */ + +- /* Before trying to open the control channel, make sure it +- * isn't already open. */ +- close_ctl_channel(); ++ // Already got them both? ++ if (control_channel >= 0 && relay_basedir_fd >= 0) ++ return 0; + +-#ifdef HAVE_OPENAT +- if (relay_basedir_fd >= 0) { +- strncpy(buf, CTL_CHANNEL_NAME, PATH_MAX - 1); +- control_channel = openat_cloexec(relay_basedir_fd, +- CTL_CHANNEL_NAME, O_RDWR, 0); +- dbug(2, "Opened %s (%d)\n", CTL_CHANNEL_NAME, control_channel); ++ // Need relay_basedir_fd .... ok try /sys/kernel/debug/systemtap/ ++ if (relay_basedir_fd < 0) { ++ char buf[PATH_MAX] = ""; ++ struct statfs st; + +- /* NB: Extra real-id access check as below */ +- if (faccessat(relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) != 0){ +- close(control_channel); +- return -5; +- } +- if (control_channel >= 0) +- goto out; /* It's OK to bypass the [f]access[at] check below, +- since this would only occur the *second* time +- staprun tries this gig, or within unprivileged stapio. */ ++ if (sprintf_chk(buf, "/sys/kernel/debug/systemtap/%s", name)) ++ return -EINVAL; ++ ++ if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC) ++ relay_basedir_fd = open (buf, O_DIRECTORY | O_RDONLY); + } +- /* PR14245, NB: we fall through to /sys ... /proc searching, +- in case the relay_basedir_fd option wasn't given (i.e., for +- early in staprun), or if errors out for some reason. */ +-#endif +- + +- // See if we have the .ctl file in debugfs +- if (sprintf_chk(buf2, "/sys/kernel/debug/systemtap/%s/%s", +- name, CTL_CHANNEL_NAME)) +- return -1; +- if (statfs("/sys/kernel/debug", &st) == 0 && (int)st.f_type == (int)DEBUGFS_MAGIC && +- (access (buf2, W_OK)==0)) { +- /* PR14245: allow subsequent operations, and if +- necessary, staprun->stapio forks, to reuse an fd for +- directory lookups (even if some parent directories have +- perms 0700. */ +- strcpy(buf, buf2); // committed ++ // Still need relay_basedir_fd ... ok try /proc/systemtap/ ++ if (relay_basedir_fd < 0) { ++ char buf[PATH_MAX] = ""; + +-#ifdef HAVE_OPENAT +- if (! sprintf_chk(buf2, "/sys/kernel/debug/systemtap/%s", name)) { +- relay_basedir_fd = open (buf2, O_DIRECTORY | O_RDONLY); +- } +-#endif +- } +- +- // PR26665: try /proc/systemtap/... also +- // (STP_TRANSPORT_1 used to use this for other purposes.) +- if (sprintf_chk(buf2, "/proc/systemtap/%s/%s", +- name, CTL_CHANNEL_NAME)) +- return -1; +- if (relay_basedir_fd < 0 && (access(buf2, W_OK)==0)) { +- strcpy(buf, buf2); // committed ++ if (sprintf_chk(buf, "/proc/systemtap/%s", name)) ++ return -EINVAL; + +-#ifdef HAVE_OPENAT +- if (! sprintf_chk(buf2, "/proc/systemtap/%s", name)) { +- relay_basedir_fd = open (buf2, O_DIRECTORY | O_RDONLY); +- } +-#endif ++ relay_basedir_fd = open (buf, O_DIRECTORY | O_RDONLY); + } + +- /* At this point, we have buf, which is the full path to the .ctl file, +- and we may have a relay_basedir_fd, which is useful to pass across +- staprun->stapio fork/execs. */ +- +- control_channel = open_cloexec(buf, O_RDWR, 0); +- dbug(2, "Opened %s (%d)\n", buf, control_channel); +- +- /* NB: Even if open() succeeded with effective-UID permissions, we +- * need the access() check to make sure real-UID permissions are also +- * sufficient. When we run under the setuid staprun, effective and +- * real UID may not be the same. Specifically, we want to prevent +- * a local stapusr from trying to attach to a different stapusr's module. +- * +- * The access() is done *after* open() to avoid any TOCTOU-style race +- * condition. We believe it's probably safe either way, as the file +- * we're trying to access connot be modified by a typical user, but +- * better safe than sorry. +- */ +-#ifdef HAVE_OPENAT +- if (control_channel >= 0 && relay_basedir_fd >= 0) { +- if (faccessat (relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) == 0) +- goto out; +- /* else fall through */ ++ // Got relay_basedir_fd, need .ctl ++ if (relay_basedir_fd >= 0) { ++ // verify that the ctl file is accessible to our real uid/gid ++ if (faccessat(relay_basedir_fd, CTL_CHANNEL_NAME, R_OK|W_OK, 0) != 0) ++ return -EPERM; ++ ++ control_channel = openat_cloexec(relay_basedir_fd, ++ CTL_CHANNEL_NAME, O_RDWR, 0); + } +-#endif +- if (control_channel >= 0 && access(buf, R_OK|W_OK) != 0) { +- close(control_channel); +- return -5; +- } + +-out: +- if (control_channel < 0) { ++ // Fell through ++ if (relay_basedir_fd < 0 || control_channel < 0) { + err(_("Cannot attach to module %s control channel; not running?\n"), + name); +- return -3; ++ return -EINVAL; + } + return 0; + } + +commit 1120422c2822be9e00d8d11cab3fb381d2ce0cce +Author: Frank Ch. Eigler +Date: Sun Dec 13 21:19:15 2020 -0500 + + PR27067 <<< corrected bug# for previous commit +commit cd5b72a538a404011d27d86ff958355ac2c45b8d +Author: Frank Ch. Eigler +Date: Sun Jan 24 14:45:54 2021 -0500 + + PR27067: set procfs traceNN files' uid/gid too + + commit e3d03db828 neglected to include the proper calls to set the + procfs traceNN files to the correct uid/gid ownership. With those + files left as uid/gid=0/0, stapio running with a user with + stapusr/stapdev privileges couldn't fopenat() those files. Now they + can again. This problem became obvious after commit 4706ab3ca5c0, + which makes STAP_TRANS_PROCFS the default. + +diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c +index 97a6e123a..69591a235 100644 +--- a/runtime/transport/procfs.c ++++ b/runtime/transport/procfs.c +@@ -336,12 +336,14 @@ __stp_procfs_relay_create_buf_file_callback(const char *filename, + if (parent != _stp_procfs_module_dir_path.dentry) + goto out; + +- pde = proc_create (filename, 0600, ++ pde = proc_create (filename, 0400, + _stp_procfs_module_dir, + & relay_procfs_operations); + if (pde == NULL) + goto out; + ++ proc_set_user(pde, KUIDT_INIT(_stp_uid), KGIDT_INIT(_stp_gid)); ++ + rc = snprintf(fullpath, sizeof(fullpath), "/proc/systemtap/%s/%s", + THIS_MODULE->name, filename); + diff --git a/SOURCES/rhbz1906662.patch b/SOURCES/rhbz1906662.patch new file mode 100644 index 0000000..a2ab1ee --- /dev/null +++ b/SOURCES/rhbz1906662.patch @@ -0,0 +1,2831 @@ +commit 374d37118ae1274077a425261ef1428151eb6d7c +Author: Sultan Alsawaf +Date: Tue Nov 10 10:03:34 2020 -0800 + + stp_utrace: disable IRQs when holding the bucket spin lock + + This lock can be acquired from inside an IRQ, leading to a deadlock: + + WARNING: inconsistent lock state + 4.14.35-1902.6.6.el7uek.x86_64.debug #2 Tainted: G OE + -------------------------------- + inconsistent {HARDIRQ-ON-W} -> {IN-HARDIRQ-W} usage. + sh/15779 [HC1[1]:SC0[0]:HE0:SE1] takes: + (&(lock)->rlock#3){?.+.}, at: [] _stp_mempool_alloc+0x35/0xab [orxray_lj_lua_fgraph_XXXXXXX] + {HARDIRQ-ON-W} state was registered at: + lock_acquire+0xe0/0x238 + _raw_spin_lock+0x3d/0x7a + utrace_task_alloc+0xa4/0xe3 [orxray_lj_lua_fgraph_XXXXXXX] + utrace_attach_task+0x136/0x194 [orxray_lj_lua_fgraph_XXXXXXX] + __stp_utrace_attach+0x57/0x216 [orxray_lj_lua_fgraph_XXXXXXX] + stap_start_task_finder+0x12e/0x33f [orxray_lj_lua_fgraph_XXXXXXX] + systemtap_module_init+0x114d/0x11f0 [orxray_lj_lua_fgraph_XXXXXXX] + _stp_handle_start+0xea/0x1c5 [orxray_lj_lua_fgraph_XXXXXXX] + _stp_ctl_write_cmd+0x28d/0x2d1 [orxray_lj_lua_fgraph_XXXXXXX] + full_proxy_write+0x67/0xbb + __vfs_write+0x3a/0x170 + vfs_write+0xc7/0x1c0 + SyS_write+0x58/0xbf + do_syscall_64+0x7e/0x22c + entry_SYSCALL_64_after_hwframe+0x16e/0x0 + irq event stamp: 9454 + hardirqs last enabled at (9453): [] _raw_write_unlock_irqrestore+0x40/0x67 + hardirqs last disabled at (9454): [] apic_timer_interrupt+0x1c7/0x1d1 + softirqs last enabled at (9202): [] __do_softirq+0x361/0x4e5 + softirqs last disabled at (9195): [] irq_exit+0xf6/0x102 + + other info that might help us debug this: + Possible unsafe locking scenario: + + CPU0 + ---- + lock(&(lock)->rlock#3); + + lock(&(lock)->rlock#3); + + *** DEADLOCK *** + + no locks held by sh/15779. + + stack backtrace: + CPU: 16 PID: 15779 Comm: sh Tainted: G OE 4.14.35-1902.6.6.el7uek.x86_64.debug #2 + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014 + Call Trace: + + dump_stack+0x81/0xb6 + print_usage_bug+0x1fc/0x20d + ? check_usage_backwards+0x130/0x12b + mark_lock+0x1f8/0x27b + __lock_acquire+0x6e7/0x165a + ? sched_clock_local+0x18/0x81 + ? perf_swevent_hrtimer+0x136/0x151 + lock_acquire+0xe0/0x238 + ? _stp_mempool_alloc+0x35/0xab [orxray_lj_lua_fgraph_XXXXXXX] + _raw_spin_lock_irqsave+0x55/0x97 + ? _stp_mempool_alloc+0x35/0xab [orxray_lj_lua_fgraph_XXXXXXX] + _stp_mempool_alloc+0x35/0xab [orxray_lj_lua_fgraph_XXXXXXX] + _stp_ctl_get_buffer+0x69/0x215 [orxray_lj_lua_fgraph_XXXXXXX] + _stp_ctl_send+0x4e/0x169 [orxray_lj_lua_fgraph_XXXXXXX] + _stp_vlog+0xac/0x143 [orxray_lj_lua_fgraph_XXXXXXX] + ? _stp_utrace_probe_cb+0xa4/0xa4 [orxray_lj_lua_fgraph_XXXXXXX] + _stp_warn+0x6a/0x88 [orxray_lj_lua_fgraph_XXXXXXX] + function___global_warn__overload_0+0x60/0xac [orxray_lj_lua_fgraph_XXXXXXX] + probe_67+0xce/0x10e [orxray_lj_lua_fgraph_XXXXXXX] + _stp_hrtimer_notify_function+0x2db/0x55f [orxray_lj_lua_fgraph_XXXXXXX] + __hrtimer_run_queues+0x132/0x5c5 + hrtimer_interrupt+0xb7/0x1ca + smp_apic_timer_interrupt+0xa5/0x35a + apic_timer_interrupt+0x1cc/0x1d1 + + +diff --git a/runtime/stp_utrace.c b/runtime/stp_utrace.c +index e2880f1e4..46ba48923 100644 +--- a/runtime/stp_utrace.c ++++ b/runtime/stp_utrace.c +@@ -490,9 +490,9 @@ static int utrace_exit(void) + rcu_read_lock(); + stap_hlist_for_each_entry_rcu(utrace, node, &bucket->head, hlist) { + utrace->freed = true; +- stp_spin_lock(&bucket->lock); ++ stp_spin_lock_irqsave(&bucket->lock, flags); + hlist_del_rcu(&utrace->hlist); +- stp_spin_unlock(&bucket->lock); ++ stp_spin_unlock_irqrestore(&bucket->lock, flags); + + utrace_cleanup(utrace); + } +@@ -724,6 +724,7 @@ static struct utrace *utrace_task_alloc(struct utrace_bucket *bucket, + struct task_struct *task) + { + struct utrace *utrace; ++ unsigned long flags; + + utrace = kmem_cache_zalloc(utrace_cachep, STP_ALLOC_FLAGS); + if (unlikely(!utrace)) +@@ -739,9 +740,9 @@ static struct utrace *utrace_task_alloc(struct utrace_bucket *bucket, + atomic_set(&utrace->resume_work_added, 0); + atomic_set(&utrace->report_work_added, 0); + +- stp_spin_lock(&bucket->lock); ++ stp_spin_lock_irqsave(&bucket->lock, flags); + hlist_add_head_rcu(&utrace->hlist, &bucket->head); +- stp_spin_unlock(&bucket->lock); ++ stp_spin_unlock_irqrestore(&bucket->lock, flags); + return utrace; + } + +@@ -768,15 +769,17 @@ static struct utrace *utrace_task_alloc(struct utrace_bucket *bucket, + */ + static void utrace_free(struct utrace_bucket *bucket, struct utrace *utrace) + { ++ unsigned long flags; ++ + if (unlikely(!utrace)) + return; + + /* Remove this utrace from the mapping list of tasks to + * struct utrace. */ + utrace->freed = true; +- stp_spin_lock(&bucket->lock); ++ stp_spin_lock_irqsave(&bucket->lock, flags); + hlist_del_rcu(&utrace->hlist); +- stp_spin_unlock(&bucket->lock); ++ stp_spin_unlock_irqrestore(&bucket->lock, flags); + + /* Free the utrace struct. */ + #ifdef STP_TF_DEBUG +commit 6a092f5ae824d4ce972c10b8681b9272e2fd67f3 +Author: Sultan Alsawaf +Date: Tue Nov 17 11:03:53 2020 -0800 + + task_finder: call _stp_vma_done() upon error to fix memory leak + + The memory allocated inside stap_initialize_vma_map() is not freed upon + error when the task finder is started because a call to _stp_vma_done() + in the error path is missing. Add it to fix the leak. + +diff --git a/task_finder.cxx b/task_finder.cxx +index d08d44a75..7c45e728b 100644 +--- a/task_finder.cxx ++++ b/task_finder.cxx +@@ -66,6 +66,7 @@ task_finder_derived_probe_group::emit_module_init (systemtap_session& s) + + s.op->newline() << "if (rc) {"; + s.op->newline(1) << "stap_stop_task_finder();"; ++ s.op->newline() << "_stp_vma_done();"; + s.op->newline(-1) << "}"; + s.op->newline(-1) << "}"; + } +commit 3c4f82ca024df4f8e213f7c77418493262d4a4d7 +Author: Sultan Alsawaf +Date: Tue Nov 24 10:50:10 2020 -0800 + + runtime_context: factor out RCU usage using a rw lock + + We can factor out the RCU insanity in here by just adding in a rw lock + and using that to synchronize _stp_runtime_contexts_free() with any code + that has the runtime context held. + +diff --git a/runtime/linux/runtime_context.h b/runtime/linux/runtime_context.h +index 41fecba81..18566957a 100644 +--- a/runtime/linux/runtime_context.h ++++ b/runtime/linux/runtime_context.h +@@ -11,15 +11,14 @@ + #ifndef _LINUX_RUNTIME_CONTEXT_H_ + #define _LINUX_RUNTIME_CONTEXT_H_ + +-#ifndef __rcu +-#define __rcu +-#endif +- +-static struct context __rcu *contexts[NR_CPUS] = { NULL }; ++/* Can't use STP_DEFINE_RWLOCK() or this might be replaced with a spin lock */ ++static DEFINE_RWLOCK(_stp_context_lock); ++static DEFINE_PER_CPU(struct context *, contexts); ++static atomic_t _stp_context_stop = ATOMIC_INIT(0); + + static int _stp_runtime_contexts_alloc(void) + { +- int cpu; ++ unsigned int cpu; + + for_each_possible_cpu(cpu) { + /* Module init, so in user context, safe to use +@@ -31,91 +30,67 @@ static int _stp_runtime_contexts_alloc(void) + (unsigned long) sizeof (struct context)); + return -ENOMEM; + } +- rcu_assign_pointer(contexts[cpu], c); ++ per_cpu(contexts, cpu) = c; + } + return 0; + } + + /* We should be free of all probes by this time, but for example the timer for + * _stp_ctl_work_callback may still be running and looking for contexts. We +- * use RCU-sched synchronization to be sure its safe to free them. */ ++ * use _stp_context_stop and a write lock to be sure its safe to free them. */ + static void _stp_runtime_contexts_free(void) + { +- // Note that 'free_contexts' is static because it is +- // (probably) too big to fit on a kernel function's stack. +- static struct context *free_contexts[NR_CPUS] = { NULL }; +- int cpu; ++ unsigned long flags; ++ unsigned int cpu; + +- /* First, save all the pointers. */ +- rcu_read_lock_sched(); +- for_each_possible_cpu(cpu) { +- free_contexts[cpu] = rcu_dereference_sched(contexts[cpu]); +- } +- rcu_read_unlock_sched(); ++ /* Sync to make sure existing readers are done */ ++ atomic_set(&_stp_context_stop, 1); ++ write_lock_irqsave(&_stp_context_lock, flags); ++ write_unlock_irqrestore(&_stp_context_lock, flags); + +- /* Now clear all pointers to prevent new readers. */ +- for_each_possible_cpu(cpu) { +- rcu_assign_pointer(contexts[cpu], NULL); +- } +- +- /* Sync to make sure existing readers are done. */ +- stp_synchronize_sched(); +- +- /* Now we can actually free the contexts. */ +- for_each_possible_cpu(cpu) { +- struct context *c = free_contexts[cpu]; +- if (c != NULL) { +- free_contexts[cpu] = NULL; +- _stp_vfree(c); +- } +- } ++ /* Now we can actually free the contexts */ ++ for_each_possible_cpu(cpu) ++ _stp_vfree(per_cpu(contexts, cpu)); + } + + static inline struct context * _stp_runtime_get_context(void) + { +- // RHBZ1788662 rcu operations are rejected in idle-cpu contexts +- // in effect: skip probe if it's in rcu-idle state +-#if defined(STAPCONF_RCU_IS_WATCHING) || LINUX_VERSION_CODE >= KERNEL_VERSION(3,13,0) // linux commit #5c173eb8 +- if (! rcu_is_watching()) +- return 0; +-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3,3,0) // linux commit #9b2e4f18 +- if (! rcu_is_cpu_idle()) +- return 0; +-#else +- ; // XXX older kernels didn't put tracepoints in idle-cpu +-#endif +- return rcu_dereference_sched(contexts[smp_processor_id()]); ++ if (atomic_read(&_stp_context_stop)) ++ return NULL; ++ ++ return per_cpu(contexts, smp_processor_id()); + } + + static struct context * _stp_runtime_entryfn_get_context(void) ++ __acquires(&_stp_context_lock) + { + struct context* __restrict__ c = NULL; +- preempt_disable (); ++ ++ if (!read_trylock(&_stp_context_lock)) ++ return NULL; ++ + c = _stp_runtime_get_context(); + if (c != NULL) { +- if (atomic_inc_return(&c->busy) == 1) { +- // NB: Notice we're not re-enabling preemption ++ if (!atomic_cmpxchg(&c->busy, 0, 1)) { ++ // NB: Notice we're not releasing _stp_context_lock + // here. We exepect the calling code to call + // _stp_runtime_entryfn_get_context() and + // _stp_runtime_entryfn_put_context() as a + // pair. + return c; + } +- atomic_dec(&c->busy); + } +- preempt_enable_no_resched(); ++ read_unlock(&_stp_context_lock); + return NULL; + } + + static inline void _stp_runtime_entryfn_put_context(struct context *c) ++ __releases(&_stp_context_lock) + { + if (c) { +- if (c == _stp_runtime_get_context()) +- atomic_dec(&c->busy); +- /* else, warn about bad state? */ +- preempt_enable_no_resched(); ++ atomic_set(&c->busy, 0); ++ read_unlock(&_stp_context_lock); + } +- return; + } + + static void _stp_runtime_context_wait(void) +@@ -130,9 +105,13 @@ static void _stp_runtime_context_wait(void) + int i; + + holdon = 0; +- rcu_read_lock_sched(); ++ read_lock(&_stp_context_lock); ++ if (atomic_read(&_stp_context_stop)) { ++ read_unlock(&_stp_context_lock); ++ break; ++ } + for_each_possible_cpu(i) { +- struct context *c = rcu_dereference_sched(contexts[i]); ++ struct context *c = per_cpu(contexts, i); + if (c != NULL + && atomic_read (& c->busy)) { + holdon = 1; +@@ -146,7 +125,7 @@ static void _stp_runtime_context_wait(void) + } + } + } +- rcu_read_unlock_sched(); ++ read_unlock(&_stp_context_lock); + + /* + * Just in case things are really really stuck, a +commit bb25d64f7b6c98ef2fc8b711f19bd6271866d727 +Author: Sultan Alsawaf +Date: Tue Dec 1 09:54:07 2020 -0800 + + runtime_context: synchronize _stp_context_stop more strictly + + We're only reading _stp_context_stop while the read lock is held, so we + can move the modification of it to inside the write lock to ensure + strict memory ordering. As such, it no longer needs to be an atomic_t + variable. + + We also don't need to disable IRQs when holding the write lock because + only read_trylock is used from IRQ context, not read_lock, so there's no + possibility of a deadlock occurring. + +diff --git a/runtime/linux/runtime_context.h b/runtime/linux/runtime_context.h +index 18566957a..e716e6d39 100644 +--- a/runtime/linux/runtime_context.h ++++ b/runtime/linux/runtime_context.h +@@ -14,7 +14,7 @@ + /* Can't use STP_DEFINE_RWLOCK() or this might be replaced with a spin lock */ + static DEFINE_RWLOCK(_stp_context_lock); + static DEFINE_PER_CPU(struct context *, contexts); +-static atomic_t _stp_context_stop = ATOMIC_INIT(0); ++static bool _stp_context_stop; + + static int _stp_runtime_contexts_alloc(void) + { +@@ -40,13 +40,12 @@ static int _stp_runtime_contexts_alloc(void) + * use _stp_context_stop and a write lock to be sure its safe to free them. */ + static void _stp_runtime_contexts_free(void) + { +- unsigned long flags; + unsigned int cpu; + + /* Sync to make sure existing readers are done */ +- atomic_set(&_stp_context_stop, 1); +- write_lock_irqsave(&_stp_context_lock, flags); +- write_unlock_irqrestore(&_stp_context_lock, flags); ++ write_lock(&_stp_context_lock); ++ _stp_context_stop = true; ++ write_unlock(&_stp_context_lock); + + /* Now we can actually free the contexts */ + for_each_possible_cpu(cpu) +@@ -55,7 +54,7 @@ static void _stp_runtime_contexts_free(void) + + static inline struct context * _stp_runtime_get_context(void) + { +- if (atomic_read(&_stp_context_stop)) ++ if (_stp_context_stop) + return NULL; + + return per_cpu(contexts, smp_processor_id()); +@@ -106,7 +105,7 @@ static void _stp_runtime_context_wait(void) + + holdon = 0; + read_lock(&_stp_context_lock); +- if (atomic_read(&_stp_context_stop)) { ++ if (_stp_context_stop) { + read_unlock(&_stp_context_lock); + break; + } +commit 0cc239e6f0fff79cb584fc857d3220402558db37 +Author: Sultan Alsawaf +Date: Tue Dec 1 18:47:04 2020 -0800 + + runtime_context: replace _stp_context_lock with an atomic variable + + We can't use any lock primitives here, such as spin locks or rw locks, + because lock_acquire() has tracepoints inside of it. This can cause a + deadlock, so we have to roll our own synchronization mechanism using an + atomic variable. + +diff --git a/runtime/linux/runtime_context.h b/runtime/linux/runtime_context.h +index e716e6d39..7dd240e1a 100644 +--- a/runtime/linux/runtime_context.h ++++ b/runtime/linux/runtime_context.h +@@ -11,10 +11,9 @@ + #ifndef _LINUX_RUNTIME_CONTEXT_H_ + #define _LINUX_RUNTIME_CONTEXT_H_ + +-/* Can't use STP_DEFINE_RWLOCK() or this might be replaced with a spin lock */ +-static DEFINE_RWLOCK(_stp_context_lock); ++/* Can't use a lock primitive for this because lock_acquire() has tracepoints */ ++static atomic_t _stp_contexts_busy_ctr = ATOMIC_INIT(0); + static DEFINE_PER_CPU(struct context *, contexts); +-static bool _stp_context_stop; + + static int _stp_runtime_contexts_alloc(void) + { +@@ -37,15 +36,14 @@ static int _stp_runtime_contexts_alloc(void) + + /* We should be free of all probes by this time, but for example the timer for + * _stp_ctl_work_callback may still be running and looking for contexts. We +- * use _stp_context_stop and a write lock to be sure its safe to free them. */ ++ * use _stp_contexts_busy_ctr to be sure its safe to free them. */ + static void _stp_runtime_contexts_free(void) + { + unsigned int cpu; + + /* Sync to make sure existing readers are done */ +- write_lock(&_stp_context_lock); +- _stp_context_stop = true; +- write_unlock(&_stp_context_lock); ++ while (atomic_cmpxchg(&_stp_contexts_busy_ctr, 0, INT_MAX)) ++ cpu_relax(); + + /* Now we can actually free the contexts */ + for_each_possible_cpu(cpu) +@@ -54,24 +52,20 @@ static void _stp_runtime_contexts_free(void) + + static inline struct context * _stp_runtime_get_context(void) + { +- if (_stp_context_stop) +- return NULL; +- + return per_cpu(contexts, smp_processor_id()); + } + + static struct context * _stp_runtime_entryfn_get_context(void) +- __acquires(&_stp_context_lock) + { + struct context* __restrict__ c = NULL; + +- if (!read_trylock(&_stp_context_lock)) ++ if (!atomic_add_unless(&_stp_contexts_busy_ctr, 1, INT_MAX)) + return NULL; + + c = _stp_runtime_get_context(); + if (c != NULL) { + if (!atomic_cmpxchg(&c->busy, 0, 1)) { +- // NB: Notice we're not releasing _stp_context_lock ++ // NB: Notice we're not releasing _stp_contexts_busy_ctr + // here. We exepect the calling code to call + // _stp_runtime_entryfn_get_context() and + // _stp_runtime_entryfn_put_context() as a +@@ -79,16 +73,15 @@ static struct context * _stp_runtime_entryfn_get_context(void) + return c; + } + } +- read_unlock(&_stp_context_lock); ++ atomic_dec(&_stp_contexts_busy_ctr); + return NULL; + } + + static inline void _stp_runtime_entryfn_put_context(struct context *c) +- __releases(&_stp_context_lock) + { + if (c) { + atomic_set(&c->busy, 0); +- read_unlock(&_stp_context_lock); ++ atomic_dec(&_stp_contexts_busy_ctr); + } + } + +@@ -104,11 +97,9 @@ static void _stp_runtime_context_wait(void) + int i; + + holdon = 0; +- read_lock(&_stp_context_lock); +- if (_stp_context_stop) { +- read_unlock(&_stp_context_lock); ++ if (!atomic_add_unless(&_stp_contexts_busy_ctr, 1, INT_MAX)) + break; +- } ++ + for_each_possible_cpu(i) { + struct context *c = per_cpu(contexts, i); + if (c != NULL +@@ -124,7 +115,7 @@ static void _stp_runtime_context_wait(void) + } + } + } +- read_unlock(&_stp_context_lock); ++ atomic_dec(&_stp_contexts_busy_ctr); + + /* + * Just in case things are really really stuck, a +commit fbab0ea35e6af0d6599c6de3708b24008bf03ae6 +Author: Sultan Alsawaf +Date: Wed Dec 2 11:07:11 2020 -0800 + + runtime_context: disable preempt while holding the context + + After the context lock was converted to an atomic in the previous + commit, the preempt disable logic disappeared. Add it back. + +diff --git a/runtime/linux/runtime_context.h b/runtime/linux/runtime_context.h +index 7dd240e1a..7a1532e54 100644 +--- a/runtime/linux/runtime_context.h ++++ b/runtime/linux/runtime_context.h +@@ -34,6 +34,24 @@ static int _stp_runtime_contexts_alloc(void) + return 0; + } + ++static bool _stp_runtime_context_trylock(void) ++{ ++ bool locked; ++ ++ preempt_disable(); ++ locked = atomic_add_unless(&_stp_contexts_busy_ctr, 1, INT_MAX); ++ if (!locked) ++ preempt_enable_no_resched(); ++ ++ return locked; ++} ++ ++static void _stp_runtime_context_unlock(void) ++{ ++ atomic_dec(&_stp_contexts_busy_ctr); ++ preempt_enable_no_resched(); ++} ++ + /* We should be free of all probes by this time, but for example the timer for + * _stp_ctl_work_callback may still be running and looking for contexts. We + * use _stp_contexts_busy_ctr to be sure its safe to free them. */ +@@ -59,7 +77,7 @@ static struct context * _stp_runtime_entryfn_get_context(void) + { + struct context* __restrict__ c = NULL; + +- if (!atomic_add_unless(&_stp_contexts_busy_ctr, 1, INT_MAX)) ++ if (!_stp_runtime_context_trylock()) + return NULL; + + c = _stp_runtime_get_context(); +@@ -73,7 +91,7 @@ static struct context * _stp_runtime_entryfn_get_context(void) + return c; + } + } +- atomic_dec(&_stp_contexts_busy_ctr); ++ _stp_runtime_context_unlock(); + return NULL; + } + +@@ -81,7 +99,7 @@ static inline void _stp_runtime_entryfn_put_context(struct context *c) + { + if (c) { + atomic_set(&c->busy, 0); +- atomic_dec(&_stp_contexts_busy_ctr); ++ _stp_runtime_context_unlock(); + } + } + +@@ -97,7 +115,7 @@ static void _stp_runtime_context_wait(void) + int i; + + holdon = 0; +- if (!atomic_add_unless(&_stp_contexts_busy_ctr, 1, INT_MAX)) ++ if (!_stp_runtime_context_trylock()) + break; + + for_each_possible_cpu(i) { +@@ -115,7 +133,7 @@ static void _stp_runtime_context_wait(void) + } + } + } +- atomic_dec(&_stp_contexts_busy_ctr); ++ _stp_runtime_context_unlock(); + + /* + * Just in case things are really really stuck, a +commit aedc044d5d38cb2fa6144d0a3345d06847862f1b +Author: Sultan Alsawaf +Date: Wed Dec 2 11:27:47 2020 -0800 + + task_finder_vma: add kfree_rcu() compat for old kernels + + Newer RHEL 6 kernels have kfree_rcu(), but older ones do not. Using + kfree_rcu() is beneficial because it lets the RCU subsystem know that + the queued RCU callback is low-priority, and can be deferred, hence why + we don't replace kfree_rcu() with call_rcu() outright. Luckily, + kfree_rcu() is a macro so we can just #ifdef with it. + +diff --git a/runtime/task_finder_vma.c b/runtime/task_finder_vma.c +index 7f0f6ed56..dc77a80f5 100644 +--- a/runtime/task_finder_vma.c ++++ b/runtime/task_finder_vma.c +@@ -87,6 +87,15 @@ __stp_tf_vma_new_entry(void) + return entry; + } + ++#ifndef kfree_rcu ++static void __stp_tf_vma_free_entry(struct rcu_head *rcu) ++{ ++ struct __stp_tf_vma_entry *entry = container_of(rcu, typeof(*entry), rcu); ++ ++ kfree(entry); ++} ++#endif ++ + // __stp_tf_vma_put_entry(): Put a specified number of references on the entry. + static void + __stp_tf_vma_put_entry(struct __stp_tf_vma_bucket *bucket, +@@ -106,7 +115,11 @@ __stp_tf_vma_put_entry(struct __stp_tf_vma_bucket *bucket, + hlist_del_rcu(&entry->hlist); + stp_spin_unlock_irqrestore(&bucket->lock, flags); + ++#ifdef kfree_rcu + kfree_rcu(entry, rcu); ++#else ++ call_rcu(&entry->rcu, __stp_tf_vma_free_entry); ++#endif + } + + // stap_initialize_vma_map(): Initialize the free list. Grabs the +commit 6a27888b118b7a94650a68aae028957cdd5fb5f5 +Author: Sultan Alsawaf +Date: Wed Dec 2 18:09:17 2020 -0800 + + REVERTME: tapset-timers: work around on-the-fly deadlocks caused by mutex_trylock + + The following deadlock exists due to tracepoints existing inside a lock + that is used both inside probe context and outside probe context: + #0 [ffff88017f6d7a08] kvm_wait at ffffffff81079f5a + #1 [ffff88017f6d7a30] __pv_queued_spin_lock_slowpath at ffffffff8114f51e + #2 [ffff88017f6d7a70] queued_spin_lock_slowpath at ffffffff810e842b + #3 [ffff88017f6d7a80] mutex_trylock at ffffffff81882b1b + #4 [ffff88017f6d7ab8] _stp_transport_trylock_relay_inode at ffffffffc0c599df [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #5 [ffff88017f6d7ad8] __stp_print_flush at ffffffffc09b6483 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #6 [ffff88017f6d7b10] probe_7879 at ffffffffc0a98c85 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #7 [ffff88017f6d7b38] enter_real_tracepoint_probe_1543 at ffffffffc0c3b757 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #8 [ffff88017f6d7b70] enter_tracepoint_probe_1543 at ffffffffc09b117e [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #9 [ffff88017f6d7b80] lock_acquire at ffffffff811460ba + #10 [ffff88017f6d7be8] mutex_trylock at ffffffff81882a27 + #11 [ffff88017f6d7c20] _stp_transport_trylock_relay_inode at ffffffffc0c599df [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #12 [ffff88017f6d7c40] __stp_print_flush at ffffffffc09b6483 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #13 [ffff88017f6d7c78] _stp_vlog at ffffffffc09b8d32 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #14 [ffff88017f6d7cd8] _stp_dbug at ffffffffc09ba43b [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #15 [ffff88017f6d7d38] systemtap_module_refresh at ffffffffc09ba51d [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #16 [ffff88017f6d7d50] module_refresher at ffffffffc09ba53e [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #17 [ffff88017f6d7d60] process_one_work at ffffffff810da9cc + #18 [ffff88017f6d7de8] worker_thread at ffffffff810dafe6 + #19 [ffff88017f6d7e48] kthread at ffffffff810e44cf + #20 [ffff88017f6d7f50] ret_from_fork_nospec_begin at ffffffff818958dd + + Note the deadlock due to _stp_transport_trylock_relay_inode recursing + onto itself via mutex_trylock. + + This is a temporary fix for the issue until a proper patch is made to + remove the mutex_trylock from __stp_print_flush. This should be reverted + when that patch lands (it will have something to do with bulkmode). + +diff --git a/tapset-timers.cxx b/tapset-timers.cxx +index 10da17cda..503498c85 100644 +--- a/tapset-timers.cxx ++++ b/tapset-timers.cxx +@@ -391,11 +391,11 @@ hrtimer_derived_probe_group::emit_module_refresh (systemtap_session& s) + s.op->newline(+1) << "struct stap_hrtimer_probe* stp = &stap_hrtimer_probes[i];"; + // timer disabled, but condition says enabled? + s.op->newline( 0) << "if (!stp->enabled && stp->probe->cond_enabled) {"; +- s.op->newline(+1) << "dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; ++ s.op->newline(+1) << "//dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_start(stp);"; + // timer enabled, but condition says disabled? + s.op->newline(-1) << "} else if (stp->enabled && !stp->probe->cond_enabled) {"; +- s.op->newline(+1) << "dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; ++ s.op->newline(+1) << "//dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_cancel(stp);"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "stp->enabled = stp->probe->cond_enabled;"; +commit 7187dcf39412fcb25c432d318be8e49a6051f055 +Author: Sultan Alsawaf +Date: Thu Dec 3 12:57:34 2020 -0800 + + runtime: fix print races in IRQ context and during print cleanup + + Prints can race when there's a print called from IRQ context or a print + called while print cleanup takes place, which can lead to garbled print + messages, out-of-bounds memory accesses, and memory use-after-free. This + is one example of racy modification of the print buffer len in IRQ + context which caused a panic due to an out-of-bounds memory access: + + BUG: unable to handle kernel paging request at ffffe8ffff621000 + IP: [] _stp_vsprint_memory+0x83/0x950 [stap_2c44636dfda18135ca3012a752599da6_13_533] + PGD 174b90067 PUD 174b8f067 PMD 174b93067 PTE 0 + Oops: 0002 [#1] SMP + CPU: 12 PID: 3468 Comm: cat Kdump: loaded Tainted: G OE ------------ 3.10.0-1127.19.1.el7.x86_64.debug #1 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20180531_142017-buildhw-08.phx2.fedoraproject.org-1.fc28 04/01/2014 + task: ffff88001f4f0000 ti: ffff88004ea5c000 task.ti: ffff88004ea5c000 + RIP: 0010:[] [] _stp_vsprint_memory+0x83/0x950 [stap_2c44636dfda18135ca3012a752599da6_13_533] + RSP: 0018:ffff88004ea5f9a8 EFLAGS: 00010082 + RAX: ffffe8ffff621001 RBX: ffffe8ffff620ff2 RCX: fffffffffffffffe + RDX: 000000000000006e RSI: ffffffffffffffff RDI: ffffc90002c23730 + RBP: ffff88004ea5fa28 R08: 00000000ffffffff R09: 0000000000000073 + R10: ffffc90002c243d7 R11: 0000000000000001 R12: ffffc90002c2373f + R13: ffffe8ffff621004 R14: 0000000000000012 R15: 00000000fffffffe + FS: 00007f8a9b1d4740(0000) GS:ffff880179e00000(0000) knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: ffffe8ffff621000 CR3: 00000000b3e3c000 CR4: 0000000000360fe0 + DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 + DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 + Call Trace: + [] ? sched_clock+0x9/0x10 + [] ? lock_release_holdtime.part.30+0xf/0x1a0 + [] function___global_trace__overload_0+0x5b0/0x1220 [stap_2c44636dfda18135ca3012a752599da6_13_533] + [] ? stp_lock_probe+0x53/0xe0 [stap_2c44636dfda18135ca3012a752599da6_13_533] + [] ? kretprobe_trampoline_holder+0x9/0x9 + [] probe_7118+0x82/0xe0 [stap_2c44636dfda18135ca3012a752599da6_13_533] + [] enter_kretprobe_common+0x256/0x490 [stap_2c44636dfda18135ca3012a752599da6_13_533] + [] ? proc_sys_open+0x51/0x60 + [] enter_kretprobe_probe+0x10/0x20 [stap_2c44636dfda18135ca3012a752599da6_13_533] + [] trampoline_handler+0x148/0x220 + [] ? proc_sys_open+0x51/0x60 + [] kretprobe_trampoline+0x25/0x57 + [] ? proc_sys_open+0x51/0x60 + [] kretprobe_trampoline_holder+0x9/0x9 + [] ? security_inode_permission+0x22/0x30 + [] ? sysctl_head_finish+0x50/0x50 + [] vfs_open+0x5d/0xb0 + [] ? may_open+0x5a/0x120 + [] do_last+0x285/0x15b0 + [] ? link_path_walk+0x27e/0x8c0 + [] path_openat+0xd0/0x5d0 + [] ? kvm_clock_read+0x33/0x40 + [] do_filp_open+0x4d/0xb0 + [] ? _raw_spin_unlock+0x27/0x40 + [] ? __alloc_fd+0xfb/0x270 + [] do_sys_open+0x124/0x220 + [] SyS_open+0x1e/0x20 + [] kretprobe_trampoline_holder+0x9/0x9 + + This patch resolves the IRQ print races by disabling IRQs on the local + CPU when accessing said CPU's print buffer, and resolves the cleanup + races with a lock. We also protect against data corruption and panics + from prints inside NMIs now by checking if the current CPU was accessing + the log buffer when an NMI fired; in this case, the NMI's prints will be + dropped, as there is no way to safely service them without creating a + dedicated log buffer for them. This is achieved by forbidding reentrancy + with respect to _stp_print_trylock_irqsave() when the runtime context + isn't held. Reentrancy is otherwise allowed when the runtime context is + held because the runtime context provides reentrancy protection. + +diff --git a/runtime/linux/io.c b/runtime/linux/io.c +index 74a032c52..122708e2a 100644 +--- a/runtime/linux/io.c ++++ b/runtime/linux/io.c +@@ -20,9 +20,6 @@ + + #define WARN_STRING "WARNING: " + #define ERR_STRING "ERROR: " +-#if (STP_LOG_BUF_LEN < 10) /* sizeof(WARN_STRING) */ +-#error "STP_LOG_BUF_LEN is too short" +-#endif + + enum code { INFO=0, WARN, ERROR, DBUG }; + +@@ -31,25 +28,37 @@ static void _stp_vlog (enum code type, const char *func, int line, const char *f + + static void _stp_vlog (enum code type, const char *func, int line, const char *fmt, va_list args) + { ++ struct _stp_log *log; ++ unsigned long flags; ++ size_t bytes_avail; + int num; +- char *buf = per_cpu_ptr(Stp_lbuf, get_cpu()); ++ char *buf; + int start = 0; + ++ if (!_stp_print_trylock_irqsave(&flags)) ++ return; ++ ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ bytes_avail = STP_BUFFER_SIZE - log->len; ++ if (unlikely(!bytes_avail)) ++ goto err_unlock; ++ ++ buf = &log->buf[log->len]; + if (type == DBUG) { +- start = _stp_snprintf(buf, STP_LOG_BUF_LEN, "%s:%d: ", func, line); ++ start = _stp_snprintf(buf, bytes_avail, "%s:%d: ", func, line); + } else if (type == WARN) { +- /* This strcpy() is OK, since we know STP_LOG_BUF_LEN +- * is > sizeof(WARN_STRING). */ +- strcpy (buf, WARN_STRING); +- start = sizeof(WARN_STRING) - 1; ++ strncpy(buf, WARN_STRING, bytes_avail); ++ start = min(bytes_avail, sizeof(WARN_STRING) - 1); + } else if (type == ERROR) { +- /* This strcpy() is OK, since we know STP_LOG_BUF_LEN +- * is > sizeof(ERR_STRING) (which is < sizeof(WARN_STRING). */ +- strcpy (buf, ERR_STRING); +- start = sizeof(ERR_STRING) - 1; ++ strncpy(buf, ERR_STRING, bytes_avail); ++ start = min(bytes_avail, sizeof(ERR_STRING) - 1); + } + +- num = vscnprintf (buf + start, STP_LOG_BUF_LEN - start - 1, fmt, args); ++ bytes_avail -= start; ++ if (unlikely(!bytes_avail)) ++ goto err_unlock; ++ ++ num = vscnprintf(buf + start, bytes_avail - 1, fmt, args); + if (num + start) { + if (buf[num + start - 1] != '\n') { + buf[num + start] = '\n'; +@@ -66,12 +75,13 @@ static void _stp_vlog (enum code type, const char *func, int line, const char *f + if (type != DBUG) { + _stp_ctl_send(STP_OOB_DATA, buf, start + num + 1); + } else { +- _stp_print(buf); +- _stp_print_flush(); ++ log->len += start + num; ++ __stp_print_flush(log); + } + #endif + } +- put_cpu(); ++err_unlock: ++ _stp_print_unlock_irqrestore(&flags); + } + + /** Prints warning. +diff --git a/runtime/linux/print.c b/runtime/linux/print.c +index 777bca8b0..2aa2f1c8d 100644 +--- a/runtime/linux/print.c ++++ b/runtime/linux/print.c +@@ -35,84 +35,179 @@ + * @{ + */ + +-typedef struct __stp_pbuf { +- uint32_t len; /* bytes used in the buffer */ ++struct _stp_log { ++ unsigned int len; /* Bytes used in the buffer */ + char buf[STP_BUFFER_SIZE]; +-} _stp_pbuf; ++ atomic_t reentrancy_lock; ++}; ++#include "print_flush.c" + +-static void *Stp_pbuf = NULL; ++static struct _stp_log *_stp_log_pcpu; ++ ++/* ++ * An atomic counter is used to synchronize every possible print buffer usage ++ * with the _stp_print_cleanup() function. The cleanup function sets the counter ++ * to INT_MAX after waiting for everything using the print buffer to finish. We ++ * cannot use a lock primitive to implement this because lock_acquire() contains ++ * tracepoints and print statements are used both inside and outside of probes. ++ * If the lock were only used inside probes, the runtime context would protect ++ * us from recursing into the lock_acquire() tracepoints and deadlocking. We ++ * instead use _stp_print_ctr as if it were a read-write lock. ++ */ ++static atomic_t _stp_print_ctr = ATOMIC_INIT(0); + +-/** private buffer for _stp_vlog() */ +-#ifndef STP_LOG_BUF_LEN +-#define STP_LOG_BUF_LEN 256 +-#endif ++/* ++ * This disables IRQs to make per-CPU print buffer accesses atomic. There is a ++ * reentrancy protection mechanism specifically for NMIs, since they can violate ++ * our atomic guarantee. Reentrancy is otherwise allowed within code sections ++ * that have the runtime context held (via _stp_runtime_entryfn_get_context()). ++ */ ++static bool _stp_print_trylock_irqsave(unsigned long *flags) ++{ ++ bool context_held = false; ++ struct _stp_log *log; ++ ++ local_irq_save(*flags); ++ if (!atomic_add_unless(&_stp_print_ctr, 1, INT_MAX)) ++ goto irq_restore; ++ ++ /* ++ * Check the per-CPU reentrancy lock for contention, unless the runtime ++ * context is already held, in which case we already have reentrancy ++ * protection. Otherwise, if the reentrancy lock is contented, that ++ * means we're either inside an NMI that fired while the current CPU was ++ * accessing the log buffer, or something is trying to nest calls to ++ * _stp_print_trylock_irqsave(). Our only choice is to reject the log ++ * access attempt in this case because log buffer corruption and panics ++ * could ensue if we're inside an NMI. ++ */ ++ if (_stp_runtime_context_trylock()) { ++ struct context *c = _stp_runtime_get_context(); ++ context_held = c && atomic_read(&c->busy); ++ _stp_runtime_context_unlock(); ++ } + +-typedef char _stp_lbuf[STP_LOG_BUF_LEN]; +-static void *Stp_lbuf = NULL; ++ /* Fall back onto the reentrancy lock if the context isn't held */ ++ if (!context_held) { ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ if (atomic_cmpxchg(&log->reentrancy_lock, 0, 1)) ++ goto print_unlock; ++ } ++ ++ return true; ++ ++print_unlock: ++ atomic_dec(&_stp_print_ctr); ++irq_restore: ++ local_irq_restore(*flags); ++ return false; ++} ++ ++static void _stp_print_unlock_irqrestore(unsigned long *flags) ++{ ++ bool context_held = false; ++ struct _stp_log *log; ++ ++ if (_stp_runtime_context_trylock()) { ++ struct context *c = _stp_runtime_get_context(); ++ context_held = c && atomic_read(&c->busy); ++ _stp_runtime_context_unlock(); ++ } ++ ++ if (!context_held) { ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ atomic_set(&log->reentrancy_lock, 0); ++ } ++ ++ atomic_dec(&_stp_print_ctr); ++ local_irq_restore(*flags); ++} + + /* create percpu print and io buffers */ + static int _stp_print_init (void) + { +- Stp_pbuf = _stp_alloc_percpu(sizeof(_stp_pbuf)); +- if (unlikely(Stp_pbuf == 0)) +- return -1; +- +- /* now initialize IO buffer used in io.c */ +- Stp_lbuf = _stp_alloc_percpu(sizeof(_stp_lbuf)); +- if (unlikely(Stp_lbuf == 0)) { +- _stp_free_percpu(Stp_pbuf); +- return -1; ++ unsigned int cpu; ++ ++ _stp_log_pcpu = _stp_alloc_percpu(sizeof(*_stp_log_pcpu)); ++ if (!_stp_log_pcpu) ++ return -ENOMEM; ++ ++ for_each_possible_cpu(cpu) { ++ struct _stp_log *log = per_cpu_ptr(_stp_log_pcpu, cpu); ++ ++ log->reentrancy_lock = (atomic_t)ATOMIC_INIT(0); + } + return 0; + } + + static void _stp_print_cleanup (void) + { +- if (Stp_pbuf) +- _stp_free_percpu(Stp_pbuf); +- if (Stp_lbuf) +- _stp_free_percpu(Stp_lbuf); +-} ++ unsigned int cpu; + +-#include "print_flush.c" ++ /* Wait for the loggers to finish modifying the print buffers */ ++ while (atomic_cmpxchg(&_stp_print_ctr, 0, INT_MAX)) ++ cpu_relax(); ++ ++ for_each_possible_cpu(cpu) { ++ struct _stp_log *log = per_cpu_ptr(_stp_log_pcpu, cpu); ++ ++ /* ++ * Flush anything that could be left in the print buffer. It is ++ * safe to do this without any kind of synchronization mechanism ++ * because nothing is using this print buffer anymore. ++ */ ++ __stp_print_flush(log); ++ } ++ ++ _stp_free_percpu(_stp_log_pcpu); ++} + + static inline void _stp_print_flush(void) + { +- stp_print_flush(per_cpu_ptr(Stp_pbuf, smp_processor_id())); ++ struct _stp_log *log; ++ unsigned long flags; ++ ++ if (!_stp_print_trylock_irqsave(&flags)) ++ return; ++ ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ __stp_print_flush(log); ++ _stp_print_unlock_irqrestore(&flags); + } ++ + #ifndef STP_MAXBINARYARGS + #define STP_MAXBINARYARGS 127 + #endif + + +-/** Reserves space in the output buffer for direct I/O. ++/** Reserves space in the output buffer for direct I/O. Must be called with ++ * _stp_print_trylock_irqsave() held. + */ + static void * _stp_reserve_bytes (int numbytes) + { +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- int size = STP_BUFFER_SIZE - pb->len; +- void * ret; ++ struct _stp_log *log; ++ char *ret; + + if (unlikely(numbytes == 0 || numbytes > STP_BUFFER_SIZE)) + return NULL; + +- if (unlikely(numbytes > size)) +- _stp_print_flush(); ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ if (unlikely(numbytes > (STP_BUFFER_SIZE - log->len))) ++ __stp_print_flush(log); + +- ret = pb->buf + pb->len; +- pb->len += numbytes; ++ ret = &log->buf[log->len]; ++ log->len += numbytes; + return ret; + } + + + static void _stp_unreserve_bytes (int numbytes) + { +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- +- if (unlikely(numbytes == 0 || numbytes > pb->len)) +- return; ++ struct _stp_log *log; + +- pb->len -= numbytes; ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ if (numbytes <= log->len) ++ log->len -= numbytes; + } + + /** Write 64-bit args directly into the output stream. +@@ -123,22 +218,25 @@ static void _stp_unreserve_bytes (int numbytes) + */ + static void _stp_print_binary (int num, ...) + { ++ unsigned long flags; + va_list vargs; + int i; + int64_t *args; +- ++ + if (unlikely(num > STP_MAXBINARYARGS)) + num = STP_MAXBINARYARGS; + +- args = _stp_reserve_bytes(num * sizeof(int64_t)); ++ if (!_stp_print_trylock_irqsave(&flags)) ++ return; + +- if (likely(args != NULL)) { ++ args = _stp_reserve_bytes(num * sizeof(int64_t)); ++ if (args) { + va_start(vargs, num); +- for (i = 0; i < num; i++) { ++ for (i = 0; i < num; i++) + args[i] = va_arg(vargs, int64_t); +- } + va_end(vargs); + } ++ _stp_print_unlock_irqrestore(&flags); + } + + /** Print into the print buffer. +@@ -149,6 +247,7 @@ static void _stp_print_binary (int num, ...) + static void _stp_printf (const char *fmt, ...) + { + va_list args; ++ + va_start(args, fmt); + _stp_vsnprintf(NULL, 0, fmt, args); + va_end(args); +@@ -160,37 +259,36 @@ static void _stp_printf (const char *fmt, ...) + + static void _stp_print (const char *str) + { +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- char *end = pb->buf + STP_BUFFER_SIZE; +- char *ptr = pb->buf + pb->len; +- char *instr = (char *)str; +- +- while (ptr < end && *instr) +- *ptr++ = *instr++; +- +- /* Did loop terminate due to lack of buffer space? */ +- if (unlikely(*instr)) { +- /* Don't break strings across subbufs. */ +- /* Restart after flushing. */ +- _stp_print_flush(); +- end = pb->buf + STP_BUFFER_SIZE; +- ptr = pb->buf + pb->len; +- instr = (char *)str; +- while (ptr < end && *instr) +- *ptr++ = *instr++; ++ struct _stp_log *log; ++ unsigned long flags; ++ ++ if (!_stp_print_trylock_irqsave(&flags)) ++ return; ++ ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ while (1) { ++ while (log->len < STP_BUFFER_SIZE && *str) ++ log->buf[log->len++] = *str++; ++ if (likely(!*str)) ++ break; ++ __stp_print_flush(log); + } +- pb->len = ptr - pb->buf; ++ _stp_print_unlock_irqrestore(&flags); + } + + static void _stp_print_char (const char c) + { +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- int size = STP_BUFFER_SIZE - pb->len; +- if (unlikely(1 >= size)) +- _stp_print_flush(); +- +- pb->buf[pb->len] = c; +- pb->len ++; ++ struct _stp_log *log; ++ unsigned long flags; ++ ++ if (!_stp_print_trylock_irqsave(&flags)) ++ return; ++ ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ if (unlikely(log->len == STP_BUFFER_SIZE)) ++ __stp_print_flush(log); ++ log->buf[log->len++] = c; ++ _stp_print_unlock_irqrestore(&flags); + } + + static void _stp_print_kernel_info(char *sname, char *vstr, int ctx, int num_probes) +diff --git a/runtime/print.h b/runtime/print.h +index ede71f033..ffdea594d 100644 +--- a/runtime/print.h ++++ b/runtime/print.h +@@ -10,6 +10,9 @@ + #ifndef _STP_PRINT_H_ + #define _STP_PRINT_H_ + ++/* The lock must be held with IRQs disabled to do any printing */ ++static bool _stp_print_trylock_irqsave(unsigned long *flags); ++static void _stp_print_unlock_irqrestore(unsigned long *flags); + static int _stp_print_init(void); + static void _stp_print_cleanup(void); + static void *_stp_reserve_bytes(int numbytes); +diff --git a/runtime/print_flush.c b/runtime/print_flush.c +index cf40a2645..acd6a32d9 100644 +--- a/runtime/print_flush.c ++++ b/runtime/print_flush.c +@@ -13,40 +13,31 @@ + * 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. ++ * @note Interrupts must be disabled to use this. + */ + +-static STP_DEFINE_SPINLOCK(_stp_print_lock); +- +-void stp_print_flush(_stp_pbuf *pb) ++static void __stp_print_flush(struct _stp_log *log) + { +- size_t len = pb->len; ++ size_t len = log->len; + void *entry = NULL; + + /* check to see if there is anything in the buffer */ + if (likely(len == 0)) + return; + +- pb->len = 0; +- +- if (unlikely(_stp_transport_get_state() != STP_TRANSPORT_RUNNING)) +- return; ++ log->len = 0; + + dbug_trans(1, "len = %zu\n", len); + + #ifdef STP_BULKMODE + #ifdef NO_PERCPU_HEADERS + { +- struct context* __restrict__ c = NULL; +- char *bufp = pb->buf; ++ char *bufp = log->buf; + int inode_locked; + +- c = _stp_runtime_entryfn_get_context(); +- + if (!(inode_locked = _stp_transport_trylock_relay_inode())) { + atomic_inc (&_stp_transport_failures); + #ifndef STP_TRANSPORT_RISKY +- _stp_runtime_entryfn_put_context(c); + return; + #endif + } +@@ -70,26 +61,20 @@ void stp_print_flush(_stp_pbuf *pb) + + if (inode_locked) + _stp_transport_unlock_relay_inode(); +- +- _stp_runtime_entryfn_put_context(c); + } + + #else /* !NO_PERCPU_HEADERS */ + + { +- struct context* __restrict__ c = NULL; +- char *bufp = pb->buf; ++ char *bufp = log->buf; + struct _stp_trace t = { .sequence = _stp_seq_inc(), + .pdu_len = len}; + size_t bytes_reserved; + int inode_locked; + +- c = _stp_runtime_entryfn_get_context(); +- + if (!(inode_locked = _stp_transport_trylock_relay_inode())) { + atomic_inc (&_stp_transport_failures); + #ifndef STP_TRANSPORT_RISKY +- _stp_runtime_entryfn_put_context(c); + return; + #endif + } +@@ -124,48 +109,24 @@ void stp_print_flush(_stp_pbuf *pb) + + if (inode_locked) + _stp_transport_unlock_relay_inode(); +- +- _stp_runtime_entryfn_put_context(c); + } + #endif /* !NO_PERCPU_HEADERS */ + + #else /* !STP_BULKMODE */ + + { +- unsigned long flags; +- struct context* __restrict__ c = NULL; +- char *bufp = pb->buf; ++ char *bufp = log->buf; + int inode_locked; + +- /* Prevent probe reentrancy on _stp_print_lock. +- * +- * Since stp_print_flush may be called from probe context, we +- * have to make sure that its lock, _stp_print_lock, can't +- * possibly be held outside probe context too. We ensure this +- * by grabbing the context here, so any probe triggered by this +- * region will appear reentrant and be skipped rather than +- * deadlock. Failure to get_context just means we're already +- * in a probe, which is fine. +- * +- * (see also _stp_ctl_send for a similar situation) +- * +- * A better solution would be to replace this +- * concurrency-control-laden effort with a lockless +- * algorithm. +- */ +- c = _stp_runtime_entryfn_get_context(); +- + if (!(inode_locked = _stp_transport_trylock_relay_inode())) { + atomic_inc (&_stp_transport_failures); + #ifndef STP_TRANSPORT_RISKY + dbug_trans(0, "discarding %zu bytes of data\n", len); +- _stp_runtime_entryfn_put_context(c); + return; + #endif + } + + dbug_trans(1, "calling _stp_data_write...\n"); +- stp_spin_lock_irqsave(&_stp_print_lock, flags); + while (len > 0) { + size_t bytes_reserved; + +@@ -182,12 +143,9 @@ void stp_print_flush(_stp_pbuf *pb) + break; + } + } +- stp_spin_unlock_irqrestore(&_stp_print_lock, flags); + + if (inode_locked) + _stp_transport_unlock_relay_inode(); +- +- _stp_runtime_entryfn_put_context(c); + } + #endif /* !STP_BULKMODE */ + } +diff --git a/runtime/stack.c b/runtime/stack.c +index 241ccf793..da23d4395 100644 +--- a/runtime/stack.c ++++ b/runtime/stack.c +@@ -690,13 +690,20 @@ static void _stp_stack_kernel_sprint(char *str, int size, struct context* c, + * then call _stp_stack_print, + * then copy the result into the output string + * and clear the print buffer. */ +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- _stp_print_flush(); ++ struct _stp_log *log; ++ unsigned long flags; + +- _stp_stack_kernel_print(c, sym_flags); ++ if (!_stp_print_trylock_irqsave(&flags)) { ++ *str = '\0'; ++ return; ++ } + +- strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len); +- pb->len = 0; ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ __stp_print_flush(log); ++ _stp_stack_kernel_print(c, sym_flags); ++ strlcpy(str, log->buf, min_t(int, size, log->len)); ++ log->len = 0; ++ _stp_print_unlock_irqrestore(&flags); + } + + static void _stp_stack_user_sprint(char *str, int size, struct context* c, +@@ -707,13 +714,20 @@ static void _stp_stack_user_sprint(char *str, int size, struct context* c, + * then call _stp_stack_print, + * then copy the result into the output string + * and clear the print buffer. */ +- _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); +- _stp_print_flush(); ++ struct _stp_log *log; ++ unsigned long flags; + +- _stp_stack_user_print(c, sym_flags); ++ if (!_stp_print_trylock_irqsave(&flags)) { ++ *str = '\0'; ++ return; ++ } + +- strlcpy(str, pb->buf, size < (int)pb->len ? size : (int)pb->len); +- pb->len = 0; ++ log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); ++ __stp_print_flush(log); ++ _stp_stack_user_print(c, sym_flags); ++ strlcpy(str, log->buf, min_t(int, size, log->len)); ++ log->len = 0; ++ _stp_print_unlock_irqrestore(&flags); + } + + #endif /* _STACK_C_ */ +diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c +index 57955334b..44e69b68c 100644 +--- a/runtime/transport/transport.c ++++ b/runtime/transport/transport.c +@@ -540,8 +540,8 @@ static void _stp_transport_close(void) + current->pid); + _stp_cleanup_and_exit(0); + _stp_unregister_ctl_channel(); ++ _stp_print_cleanup(); /* Requires the transport, so free this first */ + _stp_transport_fs_close(); +- _stp_print_cleanup(); /* free print buffers */ + _stp_mem_debug_done(); + + dbug_trans(1, "---- CLOSED ----\n"); +diff --git a/runtime/vsprintf.c b/runtime/vsprintf.c +index 28fd18f16..417d9f7f3 100644 +--- a/runtime/vsprintf.c ++++ b/runtime/vsprintf.c +@@ -542,6 +542,8 @@ _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + int num_bytes = 0; ++ unsigned long irqflags = 0; ++ bool got_print_lock = false; + + /* Reject out-of-range values early */ + if (unlikely((int) size < 0)) +@@ -724,11 +726,14 @@ _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) + num_bytes = STP_BUFFER_SIZE; + } + ++ if (!_stp_print_trylock_irqsave(&irqflags)) ++ return 0; + str = (char*)_stp_reserve_bytes(num_bytes); + if (str == NULL) { + _stp_error("Couldn't reserve any print buffer space\n"); +- return 0; ++ goto err_unlock; + } ++ got_print_lock = true; + size = num_bytes; + end = str + size - 1; + +@@ -820,8 +825,10 @@ _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) + field_width, precision, + *fmt, flags); + if (unlikely(str == NULL)) { +- if (num_bytes > 0) ++ if (num_bytes > 0) { + _stp_unreserve_bytes(num_bytes); ++ goto err_unlock; ++ } + return 0; + } + continue; +@@ -923,7 +930,14 @@ _stp_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) + /* don't write out a null byte if the buf size is zero */ + *end = '\0'; + } ++ ++ if (got_print_lock) ++ _stp_print_unlock_irqrestore(&irqflags); + return str-buf; ++ ++err_unlock: ++ _stp_print_unlock_irqrestore(&irqflags); ++ return 0; + } + + #endif /* _VSPRINTF_C_ */ +diff --git a/translate.cxx b/translate.cxx +index 53f1d0725..f0195486c 100644 +--- a/translate.cxx ++++ b/translate.cxx +@@ -1354,6 +1354,9 @@ c_unparser::emit_compiled_printfs () + o->newline() << "unsigned long ptr_value;"; + o->newline() << "int num_bytes;"; + ++ if (print_to_stream) ++ o->newline() << "unsigned long irqflags;"; ++ + o->newline() << "(void) width;"; + o->newline() << "(void) precision;"; + o->newline() << "(void) ptr_value;"; +@@ -1452,7 +1455,9 @@ c_unparser::emit_compiled_printfs () + } + + o->newline() << "num_bytes = clamp(num_bytes, 0, STP_BUFFER_SIZE);"; +- o->newline() << "str = (char*)_stp_reserve_bytes(num_bytes);"; ++ o->newline() << "if (!_stp_print_trylock_irqsave(&irqflags))"; ++ o->newline(1) << "return;"; ++ o->newline(-1) << "str = (char*)_stp_reserve_bytes(num_bytes);"; + o->newline() << "end = str ? str + num_bytes - 1 : 0;"; + } + else // !print_to_stream +@@ -1547,8 +1552,14 @@ c_unparser::emit_compiled_printfs () + o->newline() << "if (unlikely(str == NULL)) {"; + o->indent(1); + if (print_to_stream) ++ { + o->newline() << "_stp_unreserve_bytes(num_bytes);"; +- o->newline() << "return;"; ++ o->newline() << "goto err_unlock;"; ++ } ++ else ++ { ++ o->newline() << "return;"; ++ } + o->newline(-1) << "}"; + break; + +@@ -1575,6 +1586,11 @@ c_unparser::emit_compiled_printfs () + + o->newline(-1) << "}"; + ++ if (print_to_stream) ++ { ++ o->newline(-1) << "err_unlock:"; ++ o->newline(1) << "_stp_print_unlock_irqrestore(&irqflags);"; ++ } + o->newline(-1) << "}"; + } + o->newline() << "#endif // STP_LEGACY_PRINT"; +commit e8c7a2067ec7fc6315ee9bc34a010ec5f0369c5c +Author: Frank Ch. Eigler +Date: Fri Dec 4 19:33:22 2020 -0500 + + testsuite pr14536.stp: toughen + + This test case stresses nesting of heavy duty processing (backtrace + printing) within kernel interrupt processing paths. It seems to + sometimes trigger problems - so let's make the test harder to make + latent problems show up more likely. Instead of quitting after the + first irq_* function hit, stick around for 10 seconds. + +diff --git a/testsuite/systemtap.base/pr14546.stp b/testsuite/systemtap.base/pr14546.stp +index 3e59a6f3a..e4c205a8f 100644 +--- a/testsuite/systemtap.base/pr14546.stp ++++ b/testsuite/systemtap.base/pr14546.stp +@@ -2,5 +2,6 @@ probe kernel.function("irq_*").call { + x = 10; y = 10; z = 10; w = 10 + $1 + assert(!(x != 10 || y != 10 || z != 10 || w != 10), "memory got corrupted by " . @1) +- exit() + } ++ ++probe timer.s(10) { exit () } +commit cd6399e621646856824ea96b11605a0f52011272 +Author: Frank Ch. Eigler +Date: Fri Dec 4 21:33:21 2020 -0500 + + dyninst transport: add _stp_print_*lock_irq* stubs + + Recent code on the transport/linux side needs a few new (stub) + functions and type decls. + +diff --git a/runtime/dyninst/print.c b/runtime/dyninst/print.c +index 9d91224ba..c78def272 100644 +--- a/runtime/dyninst/print.c ++++ b/runtime/dyninst/print.c +@@ -107,4 +107,18 @@ static void _stp_print_char (const char c) + } + } + ++ ++/* no-op stub synchronization */ ++static bool _stp_print_trylock_irqsave(unsigned long *flags) ++{ ++ (void) flags; ++ return true; ++} ++ ++static void _stp_print_unlock_irqrestore(unsigned long *flags) ++{ ++ (void) flags; ++} ++ ++ + #endif /* _STAPDYN_PRINT_C_ */ +diff --git a/runtime/dyninst/runtime_defines.h b/runtime/dyninst/runtime_defines.h +index 5c3dec519..d00c76a21 100644 +--- a/runtime/dyninst/runtime_defines.h ++++ b/runtime/dyninst/runtime_defines.h +@@ -7,3 +7,6 @@ + #define STAPCONF_PAGEFAULT_DISABLE 1 + #define pagefault_disable() + #define pagefault_enable() ++ ++typedef int bool; ++enum { false=0, true=1 }; +commit fd93cf71df80f7bb5aae707ea5a5875727a85770 +Author: Sultan Alsawaf +Date: Wed Dec 9 12:55:10 2020 -0800 + + PR26844: fix off-by-one error when copying printed backtraces + + Since log->buf isn't null-terminated, log->len represents the total + number of bytes present in the log buffer for copying. The use of + strlcpy() here with log->len as its size results in log->len - 1 bytes + being copied, with the log->len'nth byte of the output buffer being set + to zero to terminate the string. Use memcpy() instead to remedy this, + while ensuring that the output buffer has space for null termination, + since the output buffer needs to be terminated. + +diff --git a/runtime/stack.c b/runtime/stack.c +index da23d4395..85883d6c4 100644 +--- a/runtime/stack.c ++++ b/runtime/stack.c +@@ -692,6 +692,7 @@ static void _stp_stack_kernel_sprint(char *str, int size, struct context* c, + * and clear the print buffer. */ + struct _stp_log *log; + unsigned long flags; ++ int bytes; + + if (!_stp_print_trylock_irqsave(&flags)) { + *str = '\0'; +@@ -701,7 +702,9 @@ static void _stp_stack_kernel_sprint(char *str, int size, struct context* c, + log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); + __stp_print_flush(log); + _stp_stack_kernel_print(c, sym_flags); +- strlcpy(str, log->buf, min_t(int, size, log->len)); ++ bytes = min_t(int, size - 1, log->len); ++ memcpy(str, log->buf, bytes); ++ str[bytes] = '\0'; + log->len = 0; + _stp_print_unlock_irqrestore(&flags); + } +@@ -716,6 +719,7 @@ static void _stp_stack_user_sprint(char *str, int size, struct context* c, + * and clear the print buffer. */ + struct _stp_log *log; + unsigned long flags; ++ int bytes; + + if (!_stp_print_trylock_irqsave(&flags)) { + *str = '\0'; +@@ -725,7 +729,9 @@ static void _stp_stack_user_sprint(char *str, int size, struct context* c, + log = per_cpu_ptr(_stp_log_pcpu, raw_smp_processor_id()); + __stp_print_flush(log); + _stp_stack_user_print(c, sym_flags); +- strlcpy(str, log->buf, min_t(int, size, log->len)); ++ bytes = min_t(int, size - 1, log->len); ++ memcpy(str, log->buf, bytes); ++ str[bytes] = '\0'; + log->len = 0; + _stp_print_unlock_irqrestore(&flags); + } +commit 8819e2a04596deb2fe427d261bebcaf3d2620dfb +Author: Sultan Alsawaf +Date: Wed Dec 9 17:22:20 2020 -0800 + + always use per-cpu bulkmode relayfs files to communicate with userspace + + Using a mutex_trylock() in __stp_print_flush() leads to a lot of havoc, + for numerous. Firstly, since __stp_print_flush() can be called from IRQ + context, holding the inode mutex from here would make the mutex owner + become nonsense, since mutex locks can only be held in contexts backed + by the scheduler. Secondly, the mutex_trylock implementation has a + spin_lock() inside of it that leads to two issues: IRQs aren't disabled + when acquiring this spin_lock(), so using it from IRQ context can lead + to a deadlock, and since spin locks can have tracepoints via + lock_acquire(), the spin_lock() can recurse on itself inside a stap + probe and deadlock, like so: + + #0 [ffff88017f6d7a08] kvm_wait at ffffffff81079f5a + #1 [ffff88017f6d7a30] __pv_queued_spin_lock_slowpath at ffffffff8114f51e + #2 [ffff88017f6d7a70] queued_spin_lock_slowpath at ffffffff810e842b + #3 [ffff88017f6d7a80] mutex_trylock at ffffffff81882b1b + #4 [ffff88017f6d7ab8] _stp_transport_trylock_relay_inode at ffffffffc0c599df [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #5 [ffff88017f6d7ad8] __stp_print_flush at ffffffffc09b6483 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #6 [ffff88017f6d7b10] probe_7879 at ffffffffc0a98c85 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #7 [ffff88017f6d7b38] enter_real_tracepoint_probe_1543 at ffffffffc0c3b757 [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #8 [ffff88017f6d7b70] enter_tracepoint_probe_1543 at ffffffffc09b117e [stap_47650d3377d05db0ab7cbbaa25765809__11657] + #9 [ffff88017f6d7b80] lock_acquire at ffffffff811460ba + + The reason the mutex_trylock() was needed in the first place was because + staprun doesn't properly use the relayfs API when reading buffers in + non-bulk mode. It tries to read all CPUs' buffers from a single thread, + when it should be reading each CPU's buffer from a thread running on + said CPU in order to utilize relayfs' synchronization guarantees, which + are made by disabling IRQs on the local CPU when a buffer is modified. + + This change makes staprun always use per-CPU threads to read print + buffers so that we don't need the mutex_trylock() in the print flush + routine, which resolves a wide variety of serious bugs. + + We also need to adjust the transport sub-buffer count to accommodate for + frequent print flushing. The sub-buffer size is now reduced to match the + log buffer size, which is 8192 by default, and the number of sub-buffers + is increased to 256. This uses exactly the same amount of memory as + before. + +diff --git a/runtime/print_flush.c b/runtime/print_flush.c +index acd6a32d9..f4d72d30f 100644 +--- a/runtime/print_flush.c ++++ b/runtime/print_flush.c +@@ -18,6 +18,7 @@ + + static void __stp_print_flush(struct _stp_log *log) + { ++ char *bufp = log->buf; + size_t len = log->len; + void *entry = NULL; + +@@ -26,126 +27,20 @@ static void __stp_print_flush(struct _stp_log *log) + return; + + log->len = 0; +- + dbug_trans(1, "len = %zu\n", len); +- +-#ifdef STP_BULKMODE +-#ifdef NO_PERCPU_HEADERS +- { +- char *bufp = log->buf; +- int inode_locked; +- +- if (!(inode_locked = _stp_transport_trylock_relay_inode())) { +- atomic_inc (&_stp_transport_failures); +-#ifndef STP_TRANSPORT_RISKY +- return; +-#endif +- } +- +- 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; +- } +- } +- +- if (inode_locked) +- _stp_transport_unlock_relay_inode(); +- } +- +-#else /* !NO_PERCPU_HEADERS */ +- +- { +- char *bufp = log->buf; +- struct _stp_trace t = { .sequence = _stp_seq_inc(), +- .pdu_len = len}; ++ do { + size_t bytes_reserved; +- int inode_locked; + +- if (!(inode_locked = _stp_transport_trylock_relay_inode())) { +- atomic_inc (&_stp_transport_failures); +-#ifndef STP_TRANSPORT_RISKY +- return; +-#endif +- } +- +- 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)); ++ bytes_reserved = _stp_data_write_reserve(len, &entry); ++ if (likely(entry && bytes_reserved)) { ++ memcpy(_stp_data_entry_data(entry), bufp, ++ bytes_reserved); + _stp_data_write_commit(entry); +- } +- else { ++ bufp += bytes_reserved; ++ len -= bytes_reserved; ++ } else { + atomic_inc(&_stp_transport_failures); +- goto done; ++ break; + } +- +- 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; +- } +- } +- +- done: +- +- if (inode_locked) +- _stp_transport_unlock_relay_inode(); +- } +-#endif /* !NO_PERCPU_HEADERS */ +- +-#else /* !STP_BULKMODE */ +- +- { +- char *bufp = log->buf; +- int inode_locked; +- +- if (!(inode_locked = _stp_transport_trylock_relay_inode())) { +- atomic_inc (&_stp_transport_failures); +-#ifndef STP_TRANSPORT_RISKY +- dbug_trans(0, "discarding %zu bytes of data\n", len); +- return; +-#endif +- } +- +- dbug_trans(1, "calling _stp_data_write...\n"); +- 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; +- } +- } +- +- if (inode_locked) +- _stp_transport_unlock_relay_inode(); +- } +-#endif /* !STP_BULKMODE */ ++ } while (len > 0); + } +diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c +index ff621f71d..2ba5eea7d 100644 +--- a/runtime/transport/relay_v2.c ++++ b/runtime/transport/relay_v2.c +@@ -67,7 +67,7 @@ static size_t __stp_relay_switch_subbuf(struct rchan_buf *buf, size_t length) + return 0; + + if (unlikely(length > buf->chan->subbuf_size)) +- goto toobig; ++ length = buf->chan->subbuf_size; + + if (buf->offset != buf->chan->subbuf_size + 1) { + buf->prev_padding = buf->chan->subbuf_size - buf->offset; +@@ -98,14 +98,7 @@ static size_t __stp_relay_switch_subbuf(struct rchan_buf *buf, size_t length) + 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) +@@ -117,24 +110,17 @@ static void __stp_relay_wakeup_readers(struct rchan_buf *buf) + + static void __stp_relay_wakeup_timer(stp_timer_callback_parameter_t unused) + { +-#ifdef STP_BULKMODE + int i; +-#endif + + if (atomic_read(&_stp_relay_data.wakeup)) { + struct rchan_buf *buf; + + atomic_set(&_stp_relay_data.wakeup, 0); +-#ifdef STP_BULKMODE + for_each_possible_cpu(i) { + buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, + i); + __stp_relay_wakeup_readers(buf); + } +-#else +- buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, 0); +- __stp_relay_wakeup_readers(buf); +-#endif + } + + if (atomic_read(&_stp_relay_data.transport_state) == STP_TRANSPORT_RUNNING) +@@ -235,55 +221,8 @@ static void _stp_transport_data_fs_stop(void) + atomic_set (&_stp_relay_data.transport_state, STP_TRANSPORT_STOPPED); + del_timer_sync(&_stp_relay_data.timer); + dbug_trans(0, "flushing...\n"); +- if (_stp_relay_data.rchan) { +- struct rchan_buf *buf; +- +- /* NB we cannot call relay_flush() directly here since +- * we need to do inode locking ourselves. +- */ +- +-#ifdef STP_BULKMODE +- unsigned int i; +- struct rchan *rchan = _stp_relay_data.rchan; +- +- for_each_possible_cpu(i) { +- buf = _stp_get_rchan_subbuf(rchan->buf, i); +- if (buf) { +- struct inode *inode = buf->dentry->d_inode; +- +- /* NB we are in the syscall context which +- * allows sleeping. The following inode +- * locking might sleep. See PR26131. */ +- _stp_lock_inode(inode); +- +- /* NB we intentionally avoids calling +- * our own __stp_relay_switch_subbuf() +- * since here we can sleep. */ +- relay_switch_subbuf(buf, 0); +- +- _stp_unlock_inode(inode); +- } +- } +-#else /* !STP_BULKMODE */ +- buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, 0); +- +- if (buf != NULL) { +- struct inode *inode = buf->dentry->d_inode; +- +- /* NB we are in the syscall context which allows +- * sleeping. The following inode locking might +- * sleep. See PR26131. */ +- _stp_lock_inode(inode); +- +- /* NB we intentionally avoids calling +- * our own __stp_relay_switch_subbuf() +- * since here we can sleep. */ +- relay_switch_subbuf(buf, 0); +- +- _stp_unlock_inode(inode); +- } +-#endif +- } ++ if (_stp_relay_data.rchan) ++ relay_flush(_stp_relay_data.rchan); + } + } + +@@ -308,9 +247,7 @@ static int _stp_transport_data_fs_init(void) + + /* Create "trace" file. */ + npages = _stp_subbuf_size * _stp_nsubbufs; +-#ifdef STP_BULKMODE + npages *= num_online_cpus(); +-#endif + npages >>= PAGE_SHIFT; + si_meminfo(&si); + #define MB(i) (unsigned long)((i) >> (20 - PAGE_SHIFT)) +@@ -347,9 +284,7 @@ static int _stp_transport_data_fs_init(void) + { + u64 relay_mem; + relay_mem = _stp_subbuf_size * _stp_nsubbufs; +-#ifdef STP_BULKMODE + relay_mem *= num_online_cpus(); +-#endif + _stp_allocated_net_memory += relay_mem; + _stp_allocated_memory += relay_mem; + } +@@ -386,12 +321,7 @@ _stp_data_write_reserve(size_t size_request, void **entry) + return -EINVAL; + + buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, +-#ifdef STP_BULKMODE +- smp_processor_id() +-#else +- 0 +-#endif +- ); ++ 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) +@@ -411,65 +341,10 @@ static unsigned char *_stp_data_entry_data(void *entry) + + static int _stp_data_write_commit(void *entry) + { +- /* Nothing to do here. */ +- return 0; +-} +- +-static noinline int _stp_transport_trylock_relay_inode(void) +-{ +- unsigned i; + struct rchan_buf *buf; +- struct inode *inode; +-#ifdef DEBUG_TRANS +- cycles_t begin; +-#endif + + buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, +-#ifdef STP_BULKMODE +- smp_processor_id() +-#else +- 0 +-#endif +- ); +- if (buf == NULL) +- return 0; +- +- inode = buf->dentry->d_inode; +- +-#ifdef DEBUG_TRANS +- begin = get_cycles(); +-#endif +- +- /* NB this bounded spinlock is needed for stream mode. it is observed +- * that almost all of the iterations needed are less than 50K iterations +- * or about 300K cycles. +- */ +- for (i = 0; i < 50 * 1000; i++) { +- if (_stp_trylock_inode(inode)) { +- dbug_trans(3, "got inode lock: i=%u: cycles: %llu", i, +- get_cycles() - begin); +- return 1; +- } +- } +- +- dbug_trans(0, "failed to get inode lock: i=%u: cycles: %llu", i, +- get_cycles() - begin); ++ smp_processor_id()); ++ __stp_relay_switch_subbuf(buf, 0); + return 0; + } +- +-static void _stp_transport_unlock_relay_inode(void) +-{ +- struct rchan_buf *buf; +- +- buf = _stp_get_rchan_subbuf(_stp_relay_data.rchan->buf, +-#ifdef STP_BULKMODE +- smp_processor_id() +-#else +- 0 +-#endif +- ); +- if (buf == NULL) +- return; +- +- _stp_unlock_inode(buf->dentry->d_inode); +-} +diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c +index 96426eb7b..1be3e9485 100644 +--- a/runtime/transport/transport.c ++++ b/runtime/transport/transport.c +@@ -49,7 +49,6 @@ static int _stp_probes_started = 0; + * transport state flag is atomic. */ + static atomic_t _stp_transport_state = ATOMIC_INIT(_STP_TS_UNINITIALIZED); + +-static inline int _stp_trylock_inode(struct inode *inode); + static inline void _stp_lock_inode(struct inode *inode); + static inline void _stp_unlock_inode(struct inode *inode); + +@@ -70,8 +69,8 @@ static inline void _stp_unlock_inode(struct inode *inode); + #include "procfs.c" + #include "control.c" + +-static unsigned _stp_nsubbufs = 8; +-static unsigned _stp_subbuf_size = 65536*4; ++static unsigned _stp_nsubbufs = 256; ++static unsigned _stp_subbuf_size = STP_BUFFER_SIZE; + + /* module parameters */ + static int _stp_bufsize; +@@ -643,23 +642,6 @@ err0: + return -1; + } + +-/* returns 1 when the lock is successfully acquired, 0 otherwise. */ +-static inline int _stp_trylock_inode(struct inode *inode) +-{ +-#ifdef STAPCONF_INODE_RWSEM +- return inode_trylock(inode); +-#else +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) +- return mutex_trylock(&inode->i_mutex); +-#else +- /* NB down_trylock() uses a different convention where 0 means +- * the lock is successfully acquired. +- */ +- return !down_trylock(&inode->i_sem); +-#endif +-#endif +-} +- + static inline void _stp_lock_inode(struct inode *inode) + { + #ifdef STAPCONF_INODE_RWSEM +diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h +index 51723b7f5..cc09fc0ae 100644 +--- a/runtime/transport/transport.h ++++ b/runtime/transport/transport.h +@@ -98,24 +98,6 @@ enum _stp_transport_state { + */ + static enum _stp_transport_state _stp_transport_get_state(void); + +-/* +- * _stp_transport_trylock_relay_inode +- * +- * This function locks the relay file inode to protect against relay readers +- * (i.e., staprun/stapio). +- * Returns whether the lock is successfully obtained. +- */ +-static noinline int _stp_transport_trylock_relay_inode(void); +- +-/* +- * _stp_transport_unlock_relay_inode +- * +- * This function releases the lock obtained by +- * _stp_transport_trylock_relay_inode. +- * should only call this when the lock is indeed obtained. +- */ +-static void _stp_transport_unlock_relay_inode(void); +- + /* + * _stp_transport_data_fs_init + * +diff --git a/staprun/relay.c b/staprun/relay.c +index 2f5f2e06a..c76e76719 100644 +--- a/staprun/relay.c ++++ b/staprun/relay.c +@@ -131,6 +131,7 @@ static void *reader_thread(void *data) + sigset_t sigs; + off_t wsize = 0; + int fnum = 0; ++ cpu_set_t cpu_mask; + + sigemptyset(&sigs); + sigaddset(&sigs,SIGUSR2); +@@ -139,21 +140,18 @@ static void *reader_thread(void *data) + sigfillset(&sigs); + sigdelset(&sigs,SIGUSR2); + +- if (bulkmode) { +- cpu_set_t cpu_mask; +- CPU_ZERO(&cpu_mask); +- CPU_SET(cpu, &cpu_mask); +- if( sched_setaffinity( 0, sizeof(cpu_mask), &cpu_mask ) < 0 ) +- _perr("sched_setaffinity"); ++ CPU_ZERO(&cpu_mask); ++ CPU_SET(cpu, &cpu_mask); ++ if( sched_setaffinity( 0, sizeof(cpu_mask), &cpu_mask ) < 0 ) ++ _perr("sched_setaffinity"); + #ifdef NEED_PPOLL +- /* Without a real ppoll, there is a small race condition that could */ +- /* block ppoll(). So use a timeout to prevent that. */ +- timeout->tv_sec = 10; +- timeout->tv_nsec = 0; ++ /* Without a real ppoll, there is a small race condition that could */ ++ /* block ppoll(). So use a timeout to prevent that. */ ++ timeout->tv_sec = 10; ++ timeout->tv_nsec = 0; + #else +- timeout = NULL; ++ timeout = NULL; + #endif +- } + + if (reader_timeout_ms && timeout) { + timeout->tv_sec = reader_timeout_ms / 1000; +@@ -358,11 +356,6 @@ int init_relayfs(void) + _err("couldn't open %s.\n", buf); + return -1; + } +- if (ncpus > 1 && bulkmode == 0) { +- _err("ncpus=%d, bulkmode = %d\n", ncpus, bulkmode); +- _err("This is inconsistent! Please file a bug report. Exiting now.\n"); +- return -1; +- } + + /* PR7097 */ + if (load_only) +commit d86b64029598f69b47d9cf4295f30b7093f38cfc +Author: Sultan Alsawaf +Date: Wed Dec 9 17:22:27 2020 -0800 + + Revert "REVERTME: tapset-timers: work around on-the-fly deadlocks caused by mutex_trylock" + + This reverts commit 6a27888b118b7a94650a68aae028957cdd5fb5f5. + + No longer needed. As promised, we're reverting this. + +diff --git a/tapset-timers.cxx b/tapset-timers.cxx +index 503498c85..10da17cda 100644 +--- a/tapset-timers.cxx ++++ b/tapset-timers.cxx +@@ -391,11 +391,11 @@ hrtimer_derived_probe_group::emit_module_refresh (systemtap_session& s) + s.op->newline(+1) << "struct stap_hrtimer_probe* stp = &stap_hrtimer_probes[i];"; + // timer disabled, but condition says enabled? + s.op->newline( 0) << "if (!stp->enabled && stp->probe->cond_enabled) {"; +- s.op->newline(+1) << "//dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; ++ s.op->newline(+1) << "dbug_otf(\"enabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_start(stp);"; + // timer enabled, but condition says disabled? + s.op->newline(-1) << "} else if (stp->enabled && !stp->probe->cond_enabled) {"; +- s.op->newline(+1) << "//dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; ++ s.op->newline(+1) << "dbug_otf(\"disabling (hrtimer) pidx %zu\\n\", stp->probe->index);"; + s.op->newline( 0) << "_stp_hrtimer_cancel(stp);"; + s.op->newline(-1) << "}"; + s.op->newline( 0) << "stp->enabled = stp->probe->cond_enabled;"; +commit 3abe2c40b2dae499aff2e31beff121fbe43f7654 +Author: Sultan Alsawaf +Date: Fri Dec 11 12:31:25 2020 -0800 + + transport: set is_global to zero even when bulkmode is disabled + + This is needed now that we always want per-cpu logger threads. When + is_global is set to a non-zero value, relay won't create per-cpu log + files. + +diff --git a/runtime/transport/debugfs.c b/runtime/transport/debugfs.c +index 28a5bf89d..bd20281a0 100644 +--- a/runtime/transport/debugfs.c ++++ b/runtime/transport/debugfs.c +@@ -256,13 +256,8 @@ __stp_debugfs_relay_create_buf_file_callback(const char *filename, + * cause relay_open() to create a single global buffer rather + * than the default set of per-cpu buffers. + */ +- if (is_global) { +-#ifdef STP_BULKMODE ++ if (is_global) + *is_global = 0; +-#else +- *is_global = 1; +-#endif +- } + + if (IS_ERR(file)) { + file = NULL; +diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c +index 262409356..b0a5d5760 100644 +--- a/runtime/transport/procfs.c ++++ b/runtime/transport/procfs.c +@@ -328,13 +328,8 @@ __stp_procfs_relay_create_buf_file_callback(const char *filename, + unsigned i = 0; + struct inode* in; + +- if (is_global) { +-#ifdef STP_BULKMODE ++ if (is_global) + *is_global = 0; +-#else +- *is_global = 1; +-#endif +- } + + if (parent != _stp_procfs_module_dir_path.dentry) + goto out; +commit a26bf7890196395d73ac193b23e271398731745d +Author: Frank Ch. Eigler +Date: Fri Dec 11 15:39:29 2020 -0500 + + relay transport: comment on STP_BULK message + + While we've eliminated any STP_BULKMODE effects from the way relayfs + files are used ("always bulkmode"), staprun/stapio still need to know + whether the user intended "stap -b" or not, so they can save files + stpd_cpu* files separately. + +diff --git a/runtime/transport/control.c b/runtime/transport/control.c +index 9343b3c28..d123bef2f 100644 +--- a/runtime/transport/control.c ++++ b/runtime/transport/control.c +@@ -88,6 +88,9 @@ static ssize_t _stp_ctl_write_cmd(struct file *file, const char __user *buf, siz + break; + + case STP_BULK: ++ // NB: this signals the runtime to save separate ++ // per-cpu files; our kernel->userspace files are now ++ // always bulkmode (trace$N files). + #ifdef STP_BULKMODE + // no action needed + break; +commit b43eb4ed690bf2421978ed2896667e45e60c3400 +Author: Cosmin Tanislav +Date: Thu Dec 10 16:48:54 2020 -0500 + + bugfix: runtime: transport: handle more error cases in module init + + Signed-off-by: Sultan Alsawaf + +diff --git a/runtime/transport/relay_v2.c b/runtime/transport/relay_v2.c +index 2ba5eea7d..27729f4c8 100644 +--- a/runtime/transport/relay_v2.c ++++ b/runtime/transport/relay_v2.c +@@ -277,6 +277,7 @@ static int _stp_transport_data_fs_init(void) + #endif /* (RELAYFS_CHANNEL_VERSION < 7) */ + if (!_stp_relay_data.rchan) { + rc = -ENOENT; ++ errk("%s: relay_open() failed: %d\n", THIS_MODULE->name, rc); + goto err; + } + /* Increment _stp_allocated_memory and _stp_allocated_net_memory to account for buffers +diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c +index 1be3e9485..f005e14e2 100644 +--- a/runtime/transport/transport.c ++++ b/runtime/transport/transport.c +@@ -552,6 +552,8 @@ static void _stp_transport_close(void) + */ + static int _stp_transport_init(void) + { ++ int ret; ++ + dbug_trans(1, "transport_init\n"); + #ifdef STAPCONF_TASK_UID + _stp_uid = current->uid; +@@ -603,20 +605,28 @@ static int _stp_transport_init(void) + dbug_trans(1, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); + } + +- if (_stp_transport_fs_init(THIS_MODULE->name) != 0) ++ ret = _stp_transport_fs_init(THIS_MODULE->name); ++ if (ret) + goto err0; + + /* create control channel */ +- if (_stp_register_ctl_channel() < 0) ++ ret = _stp_register_ctl_channel(); ++ if (ret < 0) + goto err1; + + /* create print buffers */ +- if (_stp_print_init() < 0) ++ ret = _stp_print_init(); ++ if (ret < 0) { ++ errk("%s: can't create print buffers!", THIS_MODULE->name); + goto err2; ++ } + + /* set _stp_module_self dynamic info */ +- if (_stp_module_update_self() < 0) ++ ret = _stp_module_update_self(); ++ if (ret < 0) { ++ errk("%s: can't update dynamic info!", THIS_MODULE->name); + goto err3; ++ } + + /* start transport */ + _stp_transport_data_fs_start(); +@@ -639,7 +649,7 @@ err2: + err1: + _stp_transport_fs_close(); + err0: +- return -1; ++ return ret; + } + + static inline void _stp_lock_inode(struct inode *inode) +commit 341bf33f14062269c52bcebaa309518d9972ca00 +Author: Frank Ch. Eigler +Date: Fri Dec 11 18:06:36 2020 -0500 + + staprun: handle more and fewer cpus better + + NR_CPUS was a hard-coded minimum and maximum on the number of CPUs + worth of trace$N files staprun/stapio would open at startup. While a + constant is useful for array sizing (and so might as well be really + large), the actual iteration should be informed by get_nprocs_conf(3). + + This patch replaces NR_CPUS with MAX_NR_CPUS (now 1024, why not), and + limits open/thread iterations to the actual number of processors. It + even prints an error if a behemoth >1K-core machine comes into being. + +diff --git a/staprun/relay.c b/staprun/relay.c +index c76e76719..3eb8df34b 100644 +--- a/staprun/relay.c ++++ b/staprun/relay.c +@@ -12,16 +12,16 @@ + + #include "staprun.h" + +-int out_fd[NR_CPUS]; ++int out_fd[MAX_NR_CPUS]; + int monitor_end = 0; +-static pthread_t reader[NR_CPUS]; +-static int relay_fd[NR_CPUS]; +-static int avail_cpus[NR_CPUS]; +-static int switch_file[NR_CPUS]; +-static pthread_mutex_t mutex[NR_CPUS]; ++static pthread_t reader[MAX_NR_CPUS]; ++static int relay_fd[MAX_NR_CPUS]; ++static int avail_cpus[MAX_NR_CPUS]; ++static int switch_file[MAX_NR_CPUS]; ++static pthread_mutex_t mutex[MAX_NR_CPUS]; + static int bulkmode = 0; + static volatile int stop_threads = 0; +-static time_t *time_backlog[NR_CPUS]; ++static time_t *time_backlog[MAX_NR_CPUS]; + static int backlog_order=0; + #define BACKLOG_MASK ((1 << backlog_order) - 1) + #define MONITORLINELENGTH 4096 +@@ -313,12 +313,19 @@ int init_relayfs(void) + if (send_request(STP_BULK, rqbuf, sizeof(rqbuf)) == 0) + bulkmode = 1; + +- /* Try to open a slew of per-cpu trace%d files. Per PR19241, we +- need to go through all potentially present CPUs up to NR_CPUS, that +- we hope is a reasonable limit. For !bulknode, "trace0" will be +- typically used. */ ++ /* Try to open a slew of per-cpu trace%d files. Per PR19241, ++ we need to go through all potentially present CPUs up to ++ get_nprocs_conf(), up to MAX_NR_CPUS (used for array ++ allocations). For !bulknode, "trace0" will be typically ++ used, prior to systemtap 4.5; after, all are used. */ + +- for (i = 0; i < NR_CPUS; i++) { ++ int nprocs = get_nprocs_conf(); ++ if (nprocs > MAX_NR_CPUS) { ++ err("Too many CPUs: get_nprocs_conf()=%d vs MAX_NR_CPUS=%d\n", nprocs, MAX_NR_CPUS); ++ return -1; ++ } ++ ++ for (i = 0; i < nprocs; i++) { + relay_fd[i] = -1; + + #ifdef HAVE_OPENAT +@@ -348,7 +355,8 @@ int init_relayfs(void) + } + } + ncpus = cpui; +- dbug(2, "ncpus=%d, bulkmode = %d\n", ncpus, bulkmode); ++ /* ncpus could be smaller than nprocs if some cpus are offline */ ++ dbug(2, "ncpus=%d, nprocs=%d, bulkmode=%d\n", ncpus, nprocs, bulkmode); + for (i = 0; i < ncpus; i++) + dbug(2, "cpui=%d, relayfd=%d\n", i, avail_cpus[i]); + +diff --git a/staprun/relay_old.c b/staprun/relay_old.c +index f0d2e918f..248e6059d 100644 +--- a/staprun/relay_old.c ++++ b/staprun/relay_old.c +@@ -14,12 +14,12 @@ + #include "staprun.h" + + /* temporary per-cpu output written here for relayfs, filebase0...N */ +-static int relay_fd[NR_CPUS]; +-static int proc_fd[NR_CPUS]; +-static FILE *percpu_tmpfile[NR_CPUS]; +-static char *relay_buffer[NR_CPUS]; +-static pthread_t reader[NR_CPUS]; +-static int switch_file[NR_CPUS]; ++static int relay_fd[MAX_NR_CPUS]; ++static int proc_fd[MAX_NR_CPUS]; ++static FILE *percpu_tmpfile[MAX_NR_CPUS]; ++static char *relay_buffer[MAX_NR_CPUS]; ++static pthread_t reader[MAX_NR_CPUS]; ++static int switch_file[MAX_NR_CPUS]; + static int bulkmode = 0; + unsigned subbuf_size = 0; + unsigned n_subbufs = 0; +@@ -37,7 +37,7 @@ static struct buf_status + { + struct _stp_buf_info info; + unsigned max_backlog; /* max # sub-buffers ready at one time */ +-} status[NR_CPUS]; ++} status[MAX_NR_CPUS]; + + + /** +@@ -461,7 +461,13 @@ int init_oldrelayfs(void) + relay_fd[0] = -1; + out_fd[0] = 0; + +- for (i = 0; i < NR_CPUS; i++) { ++ int nprocs = get_nprocs_conf(); ++ if (nprocs > MAX_NR_CPUS) { ++ err("Too many CPUs: get_nprocs_conf()=%d vs MAX_NR_CPUS=%d\n", nprocs, MAX_NR_CPUS); ++ goto err; ++ } ++ ++ for (i = 0; i < nprocs; i++) { + int ret = open_relayfs_files(i, relay_filebase, proc_filebase); + if (ret == 0) + break; +@@ -472,7 +478,8 @@ int init_oldrelayfs(void) + } + + ncpus = i; +- dbug(2, "ncpus=%d\n", ncpus); ++ /* ncpus could be smaller than nprocs if some cpus are offline */ ++ dbug(2, "ncpus=%d, nprocs=%d\n", ncpus, nprocs); + + if (ncpus == 0) { + err("Couldn't open relayfs files.\n"); +diff --git a/staprun/stap_merge.c b/staprun/stap_merge.c +index 7507f0e3d..87de7d465 100644 +--- a/staprun/stap_merge.c ++++ b/staprun/stap_merge.c +@@ -31,15 +31,15 @@ static void usage (char *prog) + } + + #define TIMESTAMP_SIZE (sizeof(int)) +-#define NR_CPUS 256 ++#define MAX_NR_CPUS 1024 + + int main (int argc, char *argv[]) + { + char *buf, *outfile_name = NULL; + int c, i, j, rc, dropped=0; +- long count=0, min, num[NR_CPUS] = { 0 }; ++ long count=0, min, num[MAX_NR_CPUS] = { 0 }; + FILE *ofp = NULL; +- FILE *fp[NR_CPUS] = { 0 }; ++ FILE *fp[MAX_NR_CPUS] = { 0 }; + int ncpus, len, verbose = 0; + int bufsize = 65536; + +@@ -67,6 +67,10 @@ int main (int argc, char *argv[]) + + i = 0; + while (optind < argc) { ++ if (i >= MAX_NR_CPUS) { ++ fprintf(stderr, "too many files (MAX_NR_CPUS=%d)\n", MAX_NR_CPUS); ++ return -1; ++ } + fp[i] = fopen(argv[optind++], "r"); + if (!fp[i]) { + fprintf(stderr, "error opening file %s.\n", argv[optind - 1]); +diff --git a/staprun/staprun.h b/staprun/staprun.h +index e05dbe5b6..2d68bf527 100644 +--- a/staprun/staprun.h ++++ b/staprun/staprun.h +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + /* Include config.h to pick up dependency for --prefix usage. */ + #include "../config.h" +@@ -285,10 +286,10 @@ extern int optopt; + extern int optind; + + /* maximum number of CPUs we can handle */ +-#define NR_CPUS 256 ++#define MAX_NR_CPUS 1024 + + /* relay*.c uses these */ +-extern int out_fd[NR_CPUS]; ++extern int out_fd[MAX_NR_CPUS]; + + /* relay_old uses these. Set in ctl.c */ + extern unsigned subbuf_size; +commit b4b5a29b51586f75de16cacdb44bdf0b3ad0478e +Author: Sultan Alsawaf +Date: Mon Dec 14 13:20:34 2020 -0800 + + staprun: use the correct out_fd when bulkmode and fsize_max aren't used + + When bulkmode and fsize_max aren't used, there is only one output fd and + it is stored at out_fd[avail_cpus[0]]. + +diff --git a/staprun/relay.c b/staprun/relay.c +index 3eb8df34b..d0202e52f 100644 +--- a/staprun/relay.c ++++ b/staprun/relay.c +@@ -232,10 +232,17 @@ static void *reader_thread(void *data) + wbuf += bytes; + wsize += bytes; + } else { +- rc = write(out_fd[cpu], wbuf, wbytes); ++ int fd; ++ /* Only bulkmode and fsize_max use per-cpu output files. Otherwise, ++ there's just a single output fd stored at out_fd[avail_cpus[0]]. */ ++ if (bulkmode || fsize_max) ++ fd = out_fd[cpu]; ++ else ++ fd = out_fd[avail_cpus[0]]; ++ rc = write(fd, wbuf, wbytes); + if (rc <= 0) { + perr("Couldn't write to output %d for cpu %d, exiting.", +- out_fd[cpu], cpu); ++ fd, cpu); + goto error_out; + } + wbytes -= rc; +commit b26b4e2c257e0bd65134eed5e51d754227a4ed3f +Author: Sultan Alsawaf +Date: Wed Dec 30 14:21:42 2020 -0800 + + task_finder2: fix panics due to broken task work cancellation + + The task_work_cancel() API uses function pointers to uniquely identify + task work structs, so there's no guarantee that a specific task work + struct we want to cancel is the one that will actually get canceled. + This issue would cause task work structs to be freed while they were + still queued up on the task's task-worker list. + + This is an example of one such panic, where the DEBUG_MEM feature + reported that a __stp_tf_task_work struct (56 bytes) wasn't freed, + because that specific task worker got canceled and instead an active + task worker got freed! + + orxray_resty_mem_X_35062: ERROR: Memory ffff8809ed388620 len=56 allocation type: kmalloc. Not freed. + BUG: unable to handle kernel paging request at ffffffffa0570877 + IP: [] 0xffffffffa0570876 + PGD 1abd067 PUD 1abe063 PMD 1028286067 PTE 0 + Oops: 0010 [#1] SMP + CPU: 3 PID: 1338 Comm: nginx Tainted: G OE ------------ 3.10.0-514.10.2.el7.x86_64.debug #1 + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.13.0-2.fc32 04/01/2014 + task: ffff880eae2d0000 ti: ffff880eaf2e4000 task.ti: ffff880eaf2e4000 + RIP: 0010:[] [] 0xffffffffa0570876 + RSP: 0018:ffff880eaf2e7d78 EFLAGS: 00010282 + RAX: 0000000000000001 RBX: 0000000000000000 RCX: 0000000000000000 + RDX: ffff8809ed388640 RSI: 0000000000000000 RDI: ffff8809ed388640 + RBP: ffff880eaf2e7da0 R08: 0000000000000000 R09: 0000000000000000 + R10: 0000000000000001 R11: ffffffffff90001c R12: ffffffff8248b1c0 + R13: ffff880eae2d0818 R14: ffff880eae2d0000 R15: 00007eff3d2490b0 + FS: 00007eff3dcd2740(0000) GS:ffff881037c00000(0000) knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: ffffffffa0570877 CR3: 0000000ebce67000 CR4: 00000000003406e0 + DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 + DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 + Stack: + ffffffff810c6544 ffff880eaf2e7f58 ffff880eaf2e7e70 ffff880eae2d0000 + 00007eff3dcb3338 ffff880eaf2e7e38 ffffffff810b31ba ffff880eaf2e7dc0 + ffffffff8106c279 ffff880eaf2e7e50 ffff880ef8a792c0 ffff880eaf2e7df8 + Call Trace: + [] ? task_work_run+0xb4/0xe0 + [] get_signal_to_deliver+0x85a/0x960 + [] ? kvm_sched_clock_read+0x9/0x20 + [] ? sched_clock_local+0x1d/0x80 + [] ? sched_clock_cpu+0xb8/0xe0 + [] do_signal+0x57/0x6e0 + [] ? int_very_careful+0x5/0xd + [] do_notify_resume+0x5f/0xb0 + [] int_signal+0x12/0x17 + Code: Bad RIP value. + RIP [] 0xffffffffa0570876 + RSP + CR2: ffffffffa0570877 + ---[ end trace 1cdf8e5b522b246e ]--- + +diff --git a/runtime/linux/task_finder2.c b/runtime/linux/task_finder2.c +index 4e0b68f7c..ecf1f77fd 100644 +--- a/runtime/linux/task_finder2.c ++++ b/runtime/linux/task_finder2.c +@@ -226,10 +226,22 @@ static void __stp_tf_cancel_all_task_work(void) + // Cancel all remaining requests. + stp_spin_lock_irqsave(&__stp_tf_task_work_list_lock, flags); + list_for_each_entry_safe(node, tmp, &__stp_tf_task_work_list, list) { +- if (stp_task_work_cancel(node->task, node->work.func)) { +- list_del(&node->list); +- _stp_kfree(node); +- } ++ struct __stp_tf_task_work *tf_work; ++ struct task_work *work; ++ ++ work = stp_task_work_cancel(node->task, node->work.func); ++ if (!work) ++ continue; ++ ++ /* ++ * There can be multiple queued task workers with the same ++ * worker func, so there's no guarantee that tf_work == node. ++ * Therefore, we can only free what stp_task_work_cancel() just ++ * gave us; freeing 'node' would be unsafe. ++ */ ++ tf_work = container_of(work, typeof(*tf_work), work); ++ list_del(&tf_work->list); ++ _stp_kfree(tf_work); + } + stp_spin_unlock_irqrestore(&__stp_tf_task_work_list_lock, flags); + } +commit 96470399a5a6fba864b90afd15eda43cdc8c8ac4 +Author: Sultan Alsawaf +Date: Wed Dec 30 15:42:11 2020 -0800 + + task_finder2: fix list corruption in __stp_tf_cancel_all_task_work() + + The previous commit (b26b4e2c2 "task_finder2: fix panics due to broken + task work cancellation") made it possible for the next node in the task + work list to be free, which would made list_for_each_entry_safe() not so + safe anymore. Using list_for_each_entry_safe() is still the fastest + approach here, so when the next node in the list happens to be freed, we + should just restart iteration on the list. + +diff --git a/runtime/linux/task_finder2.c b/runtime/linux/task_finder2.c +index ecf1f77fd..83fc17b5e 100644 +--- a/runtime/linux/task_finder2.c ++++ b/runtime/linux/task_finder2.c +@@ -225,6 +225,7 @@ static void __stp_tf_cancel_all_task_work(void) + + // Cancel all remaining requests. + stp_spin_lock_irqsave(&__stp_tf_task_work_list_lock, flags); ++restart: + list_for_each_entry_safe(node, tmp, &__stp_tf_task_work_list, list) { + struct __stp_tf_task_work *tf_work; + struct task_work *work; +@@ -242,6 +243,21 @@ static void __stp_tf_cancel_all_task_work(void) + tf_work = container_of(work, typeof(*tf_work), work); + list_del(&tf_work->list); + _stp_kfree(tf_work); ++ ++ /* ++ * If the tf_work we just freed was the next node in the list, ++ * then we need to restart the list iteration because ++ * list_for_each_entry_safe() can't cope with the next node ++ * being freed. We still need to use list_for_each_entry_safe() ++ * because we need to get through one successful pass through ++ * the entire list, since it's not guaranteed that this list ++ * will be empty when this function exits, as there can still be ++ * active task workers running, which is fine since the ++ * stp_task_work API will wait for all task workers to finish ++ * before allowing the module to unload. ++ */ ++ if (tf_work == tmp) ++ goto restart; + } + stp_spin_unlock_irqrestore(&__stp_tf_task_work_list_lock, flags); + } +commit 6cb54128e005d1220a7b064ee42b9f72561c28e7 +Author: Sultan Alsawaf +Date: Wed Dec 30 15:47:58 2020 -0800 + + task_finder2: fix task worker race on module unload + + Unfortunately, __stp_tf_cancel_all_task_work() does not guarantee that + all of the task finder's task workers will be finished executing when it + returns. In this case, we rely on the stp_task_work API to prevent the + module from being unloaded while there are task workers in-flight, which + works, but the stp_task_work API is notified of a task worker finishing + before it actually finishes. Inside __stp_tf_task_worker_fn(), the + call to the task worker's function (tf_work->func) is where the final + refcount in the stp_task_work API could be put, but there will still be + instructions left in the task worker that will be executing for a short + time after that. In that short time, there can be a race where the + module is unloaded before the task worker finishes executing all of its + instructions, especially if the task worker gets preempted during this + time on a PREEMPT kernel. + + To remedy this, we must ensure that the last instruction in + __stp_tf_task_worker_fn() is where the stp_task_work API is notified of + a task worker finishing. + +diff --git a/runtime/linux/task_finder2.c b/runtime/linux/task_finder2.c +index 83fc17b5e..2bab19295 100644 +--- a/runtime/linux/task_finder2.c ++++ b/runtime/linux/task_finder2.c +@@ -150,6 +150,7 @@ __stp_tf_task_worker_fn(struct task_work *work) + * workers for this task. + */ + __stp_tf_task_work_free(work); ++ stp_task_work_func_done(); + } + + static void +@@ -1066,11 +1067,8 @@ __stp_tf_clone_worker(struct task_work *work) + + might_sleep(); + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING +- || current->flags & PF_EXITING) { +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); ++ || current->flags & PF_EXITING) + return; +- } + + __stp_tf_handler_start(); + +@@ -1085,10 +1083,6 @@ __stp_tf_clone_worker(struct task_work *work) + } + + __stp_tf_handler_end(); +- +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); +- return; + } + + +@@ -1392,11 +1386,8 @@ __stp_tf_quiesce_worker(struct task_work *work) + + might_sleep(); + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING +- || current->flags & PF_EXITING) { +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); ++ || current->flags & PF_EXITING) + return; +- } + + /* If we had a build-id based executable probe (so we have a + * tgt->build_id) set, we could not check it back in +@@ -1420,8 +1411,6 @@ __stp_tf_quiesce_worker(struct task_work *work) + (long) current->tgid, ok); + if (!ok) { + // stap_utrace_detach (current, & tgt->ops); +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); + return; + } + } +@@ -1444,10 +1433,6 @@ __stp_tf_quiesce_worker(struct task_work *work) + __stp_call_callbacks(tgt, current, 1, (current->pid == current->tgid)); + + __stp_tf_handler_end(); +- +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); +- return; + } + + static u32 +@@ -1614,18 +1599,12 @@ __stp_tf_mmap_worker(struct task_work *work) + + // See if we can find saved syscall info. + entry = __stp_tf_get_map_entry(current); +- if (entry == NULL) { +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); ++ if (entry == NULL) + return; +- } + + if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING + || current->flags & PF_EXITING) { + __stp_tf_remove_map_entry(entry); +- +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); + return; + } + +@@ -1650,10 +1629,6 @@ __stp_tf_mmap_worker(struct task_work *work) + __stp_tf_remove_map_entry(entry); + + __stp_tf_handler_end(); +- +- /* Remember that this task_work_func is finished. */ +- stp_task_work_func_done(); +- return; + } + + static u32 diff --git a/SOURCES/rhbz1908904.patch b/SOURCES/rhbz1908904.patch new file mode 100644 index 0000000..7cbe564 --- /dev/null +++ b/SOURCES/rhbz1908904.patch @@ -0,0 +1,44 @@ +commit e3287bddcdc51705bb206479daa82e97fb28071f +Author: Frank Ch. Eigler +Date: Wed Dec 9 22:29:43 2020 -0500 + + PR27044: fix lock loop for conditional probes + + Emit a nested block carefully so that the "goto out;" from a failed + stp_lock_probe() call in that spot near the epilogue of a + probe-handler goes downward, not upward. + +diff --git a/translate.cxx b/translate.cxx +index f0195486c..d7fe1e3b2 100644 +--- a/translate.cxx ++++ b/translate.cxx +@@ -2969,21 +2969,21 @@ c_unparser::emit_probe (derived_probe* v) + { + // PR26296 + // emit all read/write locks for global variables ... if somehow still not done by now ++ // emit a local out: label, for error catching in these condition exprs ++ o->newline() << "{"; ++ o->newline(1) << "__label__ out, deref_fault;"; + if (v->needs_global_locks ()) + emit_lock (); + + for (set::const_iterator +- it = v->probes_with_affected_conditions.begin(); +- it != v->probes_with_affected_conditions.end(); ++it) ++ it = v->probes_with_affected_conditions.begin(); ++ it != v->probes_with_affected_conditions.end(); ++it) + { +- // emit a local out: label, for error catching in these condition exprs +- o->newline() << "{"; +- o->newline(1) << "__label__ out, deref_fault;"; + emit_probe_condition_update(*it); +- o->newline(-1) << "deref_fault: __attribute__((unused));"; +- o->newline() << "out: __attribute__((unused));"; +- o->newline() << "}"; + } ++ o->newline(-1) << "deref_fault: __attribute__((unused));"; ++ o->newline() << "out: __attribute__((unused));"; ++ o->newline() << "}"; + } + + // PR26296 diff --git a/SPECS/systemtap.spec b/SPECS/systemtap.spec new file mode 100644 index 0000000..c8a5fdf --- /dev/null +++ b/SPECS/systemtap.spec @@ -0,0 +1,1636 @@ +%{!?with_sqlite: %global with_sqlite 0%{?fedora} >= 17 || 0%{?rhel} >= 7} +# prefer prebuilt docs +%{!?with_docs: %global with_docs 0} +%{!?with_htmldocs: %global with_htmldocs 0} +%{!?with_monitor: %global with_monitor 1} +# crash is not available +%ifarch ppc ppc64 %{sparc} %{mips} +%{!?with_crash: %global with_crash 0} +%else +%{!?with_crash: %global with_crash 1} +%endif +%{!?with_rpm: %global with_rpm 1} +%{!?elfutils_version: %global elfutils_version 0.142} +%{!?pie_supported: %global pie_supported 1} +%{!?with_boost: %global with_boost 0} +%ifarch %{ix86} x86_64 ppc ppc64 ppc64le aarch64 +%{!?with_dyninst: %global with_dyninst 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%else +%{!?with_dyninst: %global with_dyninst 0} +%endif +%{!?with_bpf: %global with_bpf 0%{?fedora} >= 22 || 0%{?rhel} >= 8} +%{!?with_systemd: %global with_systemd 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_emacsvim: %global with_emacsvim 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_java: %global with_java 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_virthost: %global with_virthost 0%{?fedora} >= 19 || 0%{?rhel} >= 7} +%{!?with_virtguest: %global with_virtguest 1} +%{!?with_dracut: %global with_dracut 0%{?fedora} >= 19 || 0%{?rhel} >= 6} +%ifarch x86_64 +%{!?with_mokutil: %global with_mokutil 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%{!?with_openssl: %global with_openssl 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%else +%{!?with_mokutil: %global with_mokutil 0} +%{!?with_openssl: %global with_openssl 0} +%endif +%{!?with_pyparsing: %global with_pyparsing 0%{?fedora} >= 18 || 0%{?rhel} >= 7} +%{!?with_python3: %global with_python3 0%{?fedora} >= 23 || 0%{?rhel} > 7} +%{!?with_python2_probes: %global with_python2_probes (0%{?fedora} <= 28 && 0%{?rhel} <= 7)} +%{!?with_python3_probes: %global with_python3_probes (0%{?fedora} >= 23 || 0%{?rhel} > 7)} +%{!?with_httpd: %global with_httpd 0} +%{!?with_specific_python: %global with_specific_python 0%{?fedora} >= 31} + +# Virt is supported on these arches, even on el7, but it's not in core EL7 +%if 0%{?rhel} <= 7 +%ifarch ppc64le aarch64 +%global with_virthost 0 +%endif +%endif + +%if 0%{?fedora} >= 18 || 0%{?rhel} >= 6 + %define initdir %{_initddir} +%else + # RHEL5 doesn't know _initddir + %define initdir %{_initrddir} +%endif + +%if %{with_virtguest} + %if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 + %define udevrulesdir /usr/lib/udev/rules.d + %else + %if 0%{?rhel} >= 6 + %define udevrulesdir /lib/udev/rules.d + %else + # RHEL5 + %define udevrulesdir /etc/udev/rules.d + %endif + %endif +%endif + +%if 0%{?fedora} >= 19 || 0%{?rhel} >= 7 + %define dracutstap %{_prefix}/lib/dracut/modules.d/99stap +%else + %define dracutstap %{_prefix}/share/dracut/modules.d/99stap +%endif + +%if 0%{?rhel} == 6 || 0%{?rhel} == 7 + %define dracutbindir /sbin +%else + %define dracutbindir %{_bindir} +%endif + +%if 0%{?rhel} == 6 + %{!?_rpmmacrodir: %define _rpmmacrodir /etc/rpm/} +%else + %{!?_rpmmacrodir: %define _rpmmacrodir %{_rpmconfigdir}/macros.d} +%endif + +# To avoid testsuite/*/*.stp has shebang which doesn't start with '/' +%define __brp_mangle_shebangs_exclude_from .stp$ + +Name: systemtap +Version: 4.4 +Release: 9%{?release_override}%{?dist} +# for version, see also configure.ac + + +# Packaging abstract: +# +# systemtap empty req:-client req:-devel +# systemtap-server /usr/bin/stap-server*, req:-devel +# systemtap-devel /usr/bin/stap, runtime, tapset, req:kernel-devel +# systemtap-runtime /usr/bin/staprun, /usr/bin/stapsh, /usr/bin/stapdyn +# systemtap-client /usr/bin/stap, samples, docs, tapset(bonus), req:-runtime +# systemtap-initscript /etc/init.d/systemtap, dracut module, req:systemtap +# systemtap-sdt-devel /usr/include/sys/sdt.h /usr/bin/dtrace +# systemtap-testsuite /usr/share/systemtap/testsuite*, req:systemtap, req:sdt-devel +# systemtap-runtime-java libHelperSDT.so, HelperSDT.jar, stapbm, req:-runtime +# systemtap-runtime-virthost /usr/bin/stapvirt, req:libvirt req:libxml2 +# systemtap-runtime-virtguest udev rules, init scripts/systemd service, req:-runtime +# systemtap-runtime-python2 HelperSDT python2 module, req:-runtime +# systemtap-runtime-python3 HelperSDT python3 module, req:-runtime +# +# Typical scenarios: +# +# stap-client: systemtap-client +# stap-server: systemtap-server +# local user: systemtap +# +# Unusual scenarios: +# +# intermediary stap-client for --remote: systemtap-client (-runtime unused) +# intermediary stap-server for --use-server: systemtap-server (-devel unused) + +Summary: Programmable system-wide instrumentation system +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Source: ftp://sourceware.org/pub/systemtap/releases/systemtap-%{version}.tar.gz + +Patch1: rhbz1873492.patch +Patch2: rhbz1898288.patch +Patch3: rhbz1902696.patch +Patch4: rhbz1908904.patch +Patch5: rhbz1906662.patch +Patch6: rhbz1650594.patch + +# Build* +BuildRequires: gcc-c++ +BuildRequires: cpio +BuildRequires: gettext-devel +BuildRequires: pkgconfig(nss) +BuildRequires: pkgconfig(avahi-client) +BuildRequires: pkgconfig(libdebuginfod) +%if %{with_dyninst} +BuildRequires: dyninst-devel >= 10.0 +BuildRequires: pkgconfig(libselinux) +%endif +%if %{with_sqlite} +BuildRequires: sqlite-devel > 3.7 +%endif +%if %{with_monitor} +BuildRequires: pkgconfig(json-c) +BuildRequires: pkgconfig(ncurses) +%endif +%if %{with_systemd} +BuildRequires: systemd +%endif +# Needed for libstd++ < 4.0, without +%if %{with_boost} +BuildRequires: boost-devel +%endif +%if %{with_crash} +BuildRequires: crash-devel zlib-devel +%endif +%if %{with_rpm} +BuildRequires: rpm-devel +%endif +BuildRequires: elfutils-devel >= %{elfutils_version} +%if %{with_docs} +BuildRequires: /usr/bin/latex /usr/bin/dvips /usr/bin/ps2pdf +%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 +BuildRequires: tex(fullpage.sty) tex(fancybox.sty) tex(bchr7t.tfm) tex(graphicx.sty) +%endif +# For the html.sty mentioned in the .tex files, even though latex2html is +# not run during the build, only during manual scripts/update-docs runs: +BuildRequires: latex2html +%if %{with_htmldocs} +# On F10, xmlto's pdf support was broken off into a sub-package, +# called 'xmlto-tex'. To avoid a specific F10 BuildReq, we'll do a +# file-based buildreq on '/usr/share/xmlto/format/fo/pdf'. +BuildRequires: xmlto /usr/share/xmlto/format/fo/pdf +%endif +%endif +%if %{with_emacsvim} +BuildRequires: emacs +%endif +%if %{with_java} +BuildRequires: jpackage-utils java-devel +%endif +%if %{with_virthost} +# BuildRequires: libvirt-devel >= 1.0.2 +BuildRequires: pkgconfig(libvirt) +BuildRequires: pkgconfig(libxml-2.0) +%endif +BuildRequires: readline-devel +%if 0%{?rhel} <= 5 +BuildRequires: pkgconfig(ncurses) +%endif +%if %{with_python2_probes} +BuildRequires: python2-devel +%if 0%{?fedora} >= 1 +BuildRequires: python2-setuptools +%else +BuildRequires: python-setuptools +%endif +%endif +%if %{with_python3_probes} +BuildRequires: python3-devel +BuildRequires: python3-setuptools +%endif +%if %{with_specific_python} +BuildRequires: /usr/bin/pathfix.py +%endif + +%if %{with_httpd} +BuildRequires: libmicrohttpd-devel +BuildRequires: libuuid-devel +%endif + +# Install requirements +Requires: systemtap-client = %{version}-%{release} +Requires: systemtap-devel = %{version}-%{release} + +%description +SystemTap is an instrumentation system for systems running Linux. +Developers can write instrumentation scripts to collect data on +the operation of the system. The base systemtap package contains/requires +the components needed to locally develop and execute systemtap scripts. + +# ------------------------------------------------------------------------ + +%package server +Summary: Instrumentation System Server +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-devel = %{version}-%{release} +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +Conflicts: systemtap-client < %{version}-%{release} +Requires: nss coreutils +Requires: zip unzip +Requires(pre): shadow-utils +BuildRequires: nss-devel avahi-devel +%if %{with_openssl} +Requires: openssl +%endif +%if %{with_systemd} +Requires: systemd +%else +Requires(post): chkconfig +Requires(preun): chkconfig +Requires(preun): initscripts +Requires(postun): initscripts +%endif + +%description server +This is the remote script compilation server component of systemtap. +It announces itself to nearby clients with avahi (if available), and +compiles systemtap scripts to kernel objects on their demand. + + +%package devel +Summary: Programmable system-wide instrumentation system - development headers, tools +License: GPLv2+ +URL: http://sourceware.org/systemtap/ + +%if 0%{?rhel} >= 8 || 0%{?fedora} >= 20 +Recommends: (kernel-debug-devel if kernel-debug) +Recommends: (kernel-devel if kernel) +%else +Requires: kernel-devel-uname-r +%endif + +Requires: gcc make +Conflicts: systemtap-client < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +# Suggest: kernel-debuginfo + +%description devel +This package contains the components needed to compile a systemtap +script from source form into executable (.ko) forms. It may be +installed on a self-contained developer workstation (along with the +systemtap-client and systemtap-runtime packages), or on a dedicated +remote server (alongside the systemtap-server package). It includes +a copy of the standard tapset library and the runtime library C files. + + +%package runtime +Summary: Programmable system-wide instrumentation system - runtime +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires(pre): shadow-utils +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-client < %{version}-%{release} + +%description runtime +SystemTap runtime contains the components needed to execute +a systemtap script that was already compiled into a module +using a local or remote systemtap-devel installation. + + +%package client +Summary: Programmable system-wide instrumentation system - client +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: zip unzip +Requires: systemtap-runtime = %{version}-%{release} +Requires: coreutils grep sed unzip zip +Requires: openssh-clients +Conflicts: systemtap-devel < %{version}-%{release} +Conflicts: systemtap-server < %{version}-%{release} +Conflicts: systemtap-runtime < %{version}-%{release} +%if %{with_mokutil} +Requires: mokutil +%endif + +%description client +This package contains/requires the components needed to develop +systemtap scripts, and compile them using a local systemtap-devel +or a remote systemtap-server installation, then run them using a +local or remote systemtap-runtime. It includes script samples and +documentation, and a copy of the tapset library for reference. + + +%package initscript +Summary: Systemtap Initscripts +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap = %{version}-%{release} +%if %{with_systemd} +Requires: systemd +%else +Requires(post): chkconfig +Requires(preun): chkconfig +Requires(preun): initscripts +Requires(postun): initscripts +%endif + +%description initscript +This package includes a SysVinit script to launch selected systemtap +scripts at system startup, along with a dracut module for early +boot-time probing if supported. + + +%package sdt-devel +Summary: Static probe support tools +License: GPLv2+ and Public Domain +URL: http://sourceware.org/systemtap/ +%if %{with_pyparsing} +%if %{with_python3} +Requires: python3-pyparsing +%else +%if 0%{?rhel} >= 7 +Requires: pyparsing +%else +Requires: python2-pyparsing +%endif +%endif +%endif + +%description sdt-devel +This package includes the header file used for static +instrumentation compiled into userspace programs and libraries, along +with the optional dtrace-compatibility preprocessor to process related +.d files into tracing-macro-laden .h headers. + + +%package testsuite +Summary: Instrumentation System Testsuite +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap = %{version}-%{release} +Requires: systemtap-sdt-devel = %{version}-%{release} +Requires: systemtap-server = %{version}-%{release} +Requires: dejagnu which elfutils grep nc +Requires: elfutils-debuginfod +Requires: gcc gcc-c++ make glibc-devel +# testsuite/systemtap.base/ptrace.exp needs strace +Requires: strace +# testsuite/systemtap.base/ipaddr.exp needs nc. Unfortunately, the rpm +# that provides nc has changed over time (from 'nc' to +# 'nmap-ncat'). So, we'll do a file-based require. +Requires: /usr/bin/nc +%ifnarch ia64 ppc64le aarch64 +%if 0%{?fedora} >= 21 || 0%{?rhel} >= 8 +# no prelink +%else +Requires: prelink +%endif +%endif +# testsuite/systemtap.server/client.exp needs avahi +Requires: avahi +%if %{with_crash} +# testsuite/systemtap.base/crash.exp needs crash +Requires: crash +%endif +%if %{with_java} +Requires: systemtap-runtime-java = %{version}-%{release} +%endif +%if %{with_python2_probes} +Requires: systemtap-runtime-python2 = %{version}-%{release} +%endif +%if %{with_python3_probes} +Requires: systemtap-runtime-python3 = %{version}-%{release} +%endif +%ifarch x86_64 +Requires: /usr/lib/libc.so +# ... and /usr/lib/libgcc_s.so.* +# ... and /usr/lib/libstdc++.so.* +%endif +%if 0%{?fedora} >= 18 +Requires: stress +%endif +# The following "meta" files for the systemtap examples run "perf": +# testsuite/systemtap.examples/hw_watch_addr.meta +# testsuite/systemtap.examples/memory/hw_watch_sym.meta +Requires: perf + +%description testsuite +This package includes the dejagnu-based systemtap stress self-testing +suite. This may be used by system administrators to thoroughly check +systemtap on the current system. + + +%if %{with_java} +%package runtime-java +Summary: Systemtap Java Runtime Support +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} +Requires: byteman > 2.0 +Requires: iproute +Requires: java-devel + +%description runtime-java +This package includes support files needed to run systemtap scripts +that probe Java processes running on the OpenJDK runtimes using Byteman. +%endif + +%if %{with_python2_probes} +%package runtime-python2 +Summary: Systemtap Python 2 Runtime Support +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%description runtime-python2 +This package includes support files needed to run systemtap scripts +that probe python 2 processes. +%endif + +%if %{with_python3_probes} +%package runtime-python3 +Summary: Systemtap Python 3 Runtime Support +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%if ! (%{with_python2_probes}) +# Provide an clean upgrade path when the python2 package is removed +Obsoletes: %{name}-runtime-python2 < %{version}-%{release} +%endif + +%description runtime-python3 +This package includes support files needed to run systemtap scripts +that probe python 3 processes. +%endif + +%if %{with_python3} +%package exporter +Summary: Systemtap-prometheus interoperation mechanism +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} + +%description exporter +This package includes files for a systemd service that manages +systemtap sessions and relays prometheus metrics from the sessions +to remote requesters on demand. +%endif + +%if %{with_virthost} +%package runtime-virthost +Summary: Systemtap Cross-VM Instrumentation - host +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: libvirt >= 1.0.2 +Requires: libxml2 + +%description runtime-virthost +This package includes the components required to run systemtap scripts +inside a libvirt-managed domain from the host without using a network +connection. +%endif + +%if %{with_virtguest} +%package runtime-virtguest +Summary: Systemtap Cross-VM Instrumentation - guest +License: GPLv2+ +URL: http://sourceware.org/systemtap/ +Requires: systemtap-runtime = %{version}-%{release} +%if %{with_systemd} +Requires(post): findutils coreutils +Requires(preun): grep coreutils +Requires(postun): grep coreutils +%else +Requires(post): chkconfig initscripts +Requires(preun): chkconfig initscripts +Requires(postun): initscripts +%endif + +%description runtime-virtguest +This package installs the services necessary on a virtual machine for a +systemtap-runtime-virthost machine to execute systemtap scripts. +%endif + +# ------------------------------------------------------------------------ + +%prep +%setup -q +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 + +%build + +# Enable/disable the dyninst pure-userspace backend +%if %{with_dyninst} +%global dyninst_config --with-dyninst +%else +%global dyninst_config --without-dyninst +%endif + +# Enable/disable the sqlite coverage testing support +%if %{with_sqlite} +%global sqlite_config --enable-sqlite +%else +%global sqlite_config --disable-sqlite +%endif + +# Enable/disable the crash extension +%if %{with_crash} +%global crash_config --enable-crash +%else +%global crash_config --disable-crash +%endif + +# Enable/disable the code to find and suggest needed rpms +%if %{with_rpm} +%global rpm_config --with-rpm +%else +%global rpm_config --without-rpm +%endif + +%if %{with_docs} +%if %{with_htmldocs} +%global docs_config --enable-docs --enable-htmldocs +%else +%global docs_config --enable-docs --disable-htmldocs +%endif +%else +%global docs_config --enable-docs=prebuilt +%endif + +# Enable pie as configure defaults to disabling it +%if %{pie_supported} +%global pie_config --enable-pie +%else +%global pie_config --disable-pie +%endif + + +%if %{with_java} +%global java_config --with-java=%{_jvmdir}/java +%else +%global java_config --without-java +%endif + +%if %{with_python3} +%global python3_config --with-python3 +%else +%global python3_config --without-python3 +%endif +%if %{with_python2_probes} +%global python2_probes_config --with-python2-probes +%else +%global python2_probes_config --without-python2-probes +%endif +%if %{with_python3_probes} +%global python3_probes_config --with-python3-probes +%else +%global python3_probes_config --without-python3-probes +%endif + +%if %{with_virthost} +%global virt_config --enable-virt +%else +%global virt_config --disable-virt +%endif + +%if %{with_dracut} +%global dracut_config --with-dracutstap=%{dracutstap} --with-dracutbindir=%{dracutbindir} +%else +%global dracut_config %{nil} +%endif + +%if %{with_httpd} +%global httpd_config --enable-httpd +%else +%global httpd_config --disable-httpd +%endif + +%if %{with_bpf} +%global bpf_config --with-bpf +%else +%global bpf_config --without-bpf +%endif + +# We don't ship compileworthy python code, just oddball samples +%global py_auto_byte_compile 0 + +%configure %{dyninst_config} %{sqlite_config} %{crash_config} %{docs_config} %{pie_config} %{rpm_config} %{java_config} %{virt_config} %{dracut_config} %{python3_config} %{python2_probes_config} %{python3_probes_config} %{httpd_config} %{bpf_config} --disable-silent-rules --with-extra-version="rpm %{version}-%{release}" +make %{?_smp_mflags} + + +%install +make DESTDIR=$RPM_BUILD_ROOT install +%find_lang %{name} +for dir in $(ls -1d $RPM_BUILD_ROOT%{_mandir}/{??,??_??}) ; do + dir=$(echo $dir | sed -e "s|^$RPM_BUILD_ROOT||") + lang=$(basename $dir) + echo "%%lang($lang) $dir/man*/*" >> %{name}.lang +done + +ln -s %{_datadir}/systemtap/examples + +# Fix paths in the example scripts. +find $RPM_BUILD_ROOT%{_datadir}/systemtap/examples -type f -name '*.stp' -print0 | xargs -0 sed -i -r -e '1s@^#!.+stap@#!%{_bindir}/stap@' + +# To make rpmlint happy, remove any .gitignore files in the testsuite. +find testsuite -type f -name '.gitignore' -print0 | xargs -0 rm -f + +# Because "make install" may install staprun with whatever mode, the +# post-processing programs rpmbuild runs won't be able to read it. +# So, we change permissions so that they can read it. We'll set the +# permissions back to 04110 in the %files section below. +chmod 755 $RPM_BUILD_ROOT%{_bindir}/staprun + +#install the useful stap-prep script +install -c -m 755 stap-prep $RPM_BUILD_ROOT%{_bindir}/stap-prep + +# Copy over the testsuite +cp -rp testsuite $RPM_BUILD_ROOT%{_datadir}/systemtap + +# We want the manuals in the special doc dir, not the generic doc install dir. +# We build it in place and then move it away so it doesn't get installed +# twice. rpm can specify itself where the (versioned) docs go with the +# %doc directive. +mkdir docs.installed +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/*.pdf docs.installed/ +%if %{with_docs} +%if %{with_htmldocs} +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/tapsets docs.installed/ +mv $RPM_BUILD_ROOT%{_datadir}/doc/systemtap/SystemTap_Beginners_Guide docs.installed/ +%endif +%endif + +install -D -m 644 macros.systemtap $RPM_BUILD_ROOT%{_rpmmacrodir}/macros.systemtap + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/stap-server +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/stap-server +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/lib/stap-server/.systemtap +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/log/stap-server +touch $RPM_BUILD_ROOT%{_localstatedir}/log/stap-server/log +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/cache/systemtap +mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/run/systemtap +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d +install -m 644 initscript/logrotate.stap-server $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/stap-server + +# If using systemd systemtap.service file, retain the old init script in %{_libexecdir} as a helper. +%if %{with_systemd} +mkdir -p $RPM_BUILD_ROOT%{_unitdir} +touch $RPM_BUILD_ROOT%{_unitdir}/systemtap.service +install -m 644 initscript/systemtap.service $RPM_BUILD_ROOT%{_unitdir}/systemtap.service +mkdir -p $RPM_BUILD_ROOT%{_sbindir} +install -m 755 initscript/systemtap $RPM_BUILD_ROOT%{_sbindir}/systemtap-service +%else +mkdir -p $RPM_BUILD_ROOT%{initdir} +install -m 755 initscript/systemtap $RPM_BUILD_ROOT%{initdir} +mkdir -p $RPM_BUILD_ROOT%{_sbindir} +ln -sf %{initdir}/systemtap $RPM_BUILD_ROOT%{_sbindir}/systemtap-service +# TODO CHECK CORRECTNESS: symlink %{_sbindir}/systemtap-service to %{initdir}/systemtap +%endif + +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/conf.d +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/script.d +install -m 644 initscript/config.systemtap $RPM_BUILD_ROOT%{_sysconfdir}/systemtap/config + +%if %{with_systemd} +mkdir -p $RPM_BUILD_ROOT%{_unitdir} +touch $RPM_BUILD_ROOT%{_unitdir}/stap-server.service +install -m 644 stap-server.service $RPM_BUILD_ROOT%{_unitdir}/stap-server.service +mkdir -p $RPM_BUILD_ROOT%{_tmpfilesdir} +install -m 644 stap-server.conf $RPM_BUILD_ROOT%{_tmpfilesdir}/stap-server.conf +%else +install -m 755 initscript/stap-server $RPM_BUILD_ROOT%{initdir} +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/stap-server/conf.d +mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig +install -m 644 initscript/config.stap-server $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/stap-server +%endif + +%if %{with_emacsvim} +mkdir -p $RPM_BUILD_ROOT%{_emacs_sitelispdir} +install -p -m 644 emacs/systemtap-mode.el* $RPM_BUILD_ROOT%{_emacs_sitelispdir} +mkdir -p $RPM_BUILD_ROOT%{_emacs_sitestartdir} +install -p -m 644 emacs/systemtap-init.el $RPM_BUILD_ROOT%{_emacs_sitestartdir}/systemtap-init.el +for subdir in ftdetect ftplugin indent syntax +do + mkdir -p $RPM_BUILD_ROOT%{_datadir}/vim/vimfiles/$subdir + install -p -m 644 vim/$subdir/*.vim $RPM_BUILD_ROOT%{_datadir}/vim/vimfiles/$subdir +done +%endif + +%if %{with_virtguest} + mkdir -p $RPM_BUILD_ROOT%{udevrulesdir} + %if %{with_systemd} + install -p -m 644 staprun/guest/99-stapsh.rules $RPM_BUILD_ROOT%{udevrulesdir} + mkdir -p $RPM_BUILD_ROOT%{_unitdir} + install -p -m 644 staprun/guest/stapsh@.service $RPM_BUILD_ROOT%{_unitdir} + %else + install -p -m 644 staprun/guest/99-stapsh-init.rules $RPM_BUILD_ROOT%{udevrulesdir} + install -p -m 755 staprun/guest/stapshd $RPM_BUILD_ROOT%{initdir} + mkdir -p $RPM_BUILD_ROOT%{_libexecdir}/systemtap + install -p -m 755 staprun/guest/stapsh-daemon $RPM_BUILD_ROOT%{_libexecdir}/systemtap + mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/modules + # Technically, this is only needed for RHEL5, in which the MODULE_ALIAS is missing, but + # it does no harm in RHEL6 as well + install -p -m 755 staprun/guest/virtio_console.modules $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/modules + %endif +%endif + +%if %{with_dracut} + mkdir -p $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/module-setup.sh $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/install $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/check $RPM_BUILD_ROOT%{dracutstap} + install -p -m 755 initscript/99stap/start-staprun.sh $RPM_BUILD_ROOT%{dracutstap} + touch $RPM_BUILD_ROOT%{dracutstap}/params.conf +%endif + +%if %{with_specific_python} +# Some files got ambiguous python shebangs, we fix them after everything else is done +pathfix.py -pni "%{__python3} %{py3_shbang_opts}" %{buildroot}%{python3_sitearch} %{buildroot}%{_bindir}/* +%endif + +%pre runtime +getent group stapusr >/dev/null || groupadd -f -g 156 -r stapusr +getent group stapsys >/dev/null || groupadd -f -g 157 -r stapsys +getent group stapdev >/dev/null || groupadd -f -g 158 -r stapdev +exit 0 + +%pre server +getent group stap-server >/dev/null || groupadd -f -g 155 -r stap-server +getent passwd stap-server >/dev/null || \ + useradd -c "Systemtap Compile Server" -u 155 -g stap-server -d %{_localstatedir}/lib/stap-server -r -s /sbin/nologin stap-server 2>/dev/null || \ + useradd -c "Systemtap Compile Server" -g stap-server -d %{_localstatedir}/lib/stap-server -r -s /sbin/nologin stap-server + +%pre testsuite +getent passwd stapusr >/dev/null || \ + useradd -c "Systemtap 'stapusr' User" -g stapusr -r -s /sbin/nologin stapusr +getent passwd stapsys >/dev/null || \ + useradd -c "Systemtap 'stapsys' User" -g stapsys -G stapusr -r -s /sbin/nologin stapsys +getent passwd stapdev >/dev/null || \ + useradd -c "Systemtap 'stapdev' User" -g stapdev -G stapusr -r -s /sbin/nologin stapdev +exit 0 + +%post server + +# We have some duplication between the %files listings for the +# ~stap-server directories and the explicit mkdir/chown/chmod bits +# here. Part of the reason may be that a preexisting stap-server +# account may well be placed somewhere other than +# %{_localstatedir}/lib/stap-server, but we'd like their permissions +# set similarly. + +test -e ~stap-server && chmod 750 ~stap-server + +if [ ! -f ~stap-server/.systemtap/rc ]; then + mkdir -p ~stap-server/.systemtap + chown stap-server:stap-server ~stap-server/.systemtap + # PR16276: guess at a reasonable number for a default --rlimit-nproc + numcpu=`/usr/bin/getconf _NPROCESSORS_ONLN` + if [ -z "$numcpu" -o "$numcpu" -lt 1 ]; then numcpu=1; fi + nproc=`expr $numcpu \* 30` + echo "--rlimit-as=614400000 --rlimit-cpu=60 --rlimit-nproc=$nproc --rlimit-stack=1024000 --rlimit-fsize=51200000" > ~stap-server/.systemtap/rc + chown stap-server:stap-server ~stap-server/.systemtap/rc +fi + +test -e %{_localstatedir}/log/stap-server/log || { + touch %{_localstatedir}/log/stap-server/log + chmod 644 %{_localstatedir}/log/stap-server/log + chown stap-server:stap-server %{_localstatedir}/log/stap-server/log +} +# Prepare the service +%if %{with_systemd} + # Note, Fedora policy doesn't allow network services enabled by default + # /bin/systemctl enable stap-server.service >/dev/null 2>&1 || : + /bin/systemd-tmpfiles --create %{_tmpfilesdir}/stap-server.conf >/dev/null 2>&1 || : +%else + /sbin/chkconfig --add stap-server +%endif +exit 0 + +%triggerin client -- systemtap-server +if test -e ~stap-server/.systemtap/ssl/server/stap.cert; then + # echo Authorizing ssl-peer/trusted-signer certificate for local systemtap-server + %{_libexecdir}/systemtap/stap-authorize-cert ~stap-server/.systemtap/ssl/server/stap.cert %{_sysconfdir}/systemtap/ssl/client >/dev/null + %{_libexecdir}/systemtap/stap-authorize-cert ~stap-server/.systemtap/ssl/server/stap.cert %{_sysconfdir}/systemtap/staprun >/dev/null +fi +exit 0 +# XXX: corresponding %triggerun? + +%preun server +# Check that this is the actual deinstallation of the package, as opposed to +# just removing the old package on upgrade. +if [ $1 = 0 ] ; then + %if %{with_systemd} + /bin/systemctl --no-reload disable stap-server.service >/dev/null 2>&1 || : + /bin/systemctl stop stap-server.service >/dev/null 2>&1 || : + %else + /sbin/service stap-server stop >/dev/null 2>&1 + /sbin/chkconfig --del stap-server + %endif +fi +exit 0 + +%postun server +# Check whether this is an upgrade of the package. +# If so, restart the service if it's running +if [ "$1" -ge "1" ] ; then + %if %{with_systemd} + /bin/systemctl condrestart stap-server.service >/dev/null 2>&1 || : + %else + /sbin/service stap-server condrestart >/dev/null 2>&1 || : + %endif +fi +exit 0 + +%post initscript +%if %{with_systemd} + /bin/systemctl enable systemtap.service >/dev/null 2>&1 || : +%else + /sbin/chkconfig --add systemtap +%endif +exit 0 + +%preun initscript +# Check that this is the actual deinstallation of the package, as opposed to +# just removing the old package on upgrade. +if [ $1 = 0 ] ; then + %if %{with_systemd} + /bin/systemctl --no-reload disable systemtap.service >/dev/null 2>&1 || : + /bin/systemctl stop systemtap.service >/dev/null 2>&1 || : + %else + /sbin/service systemtap stop >/dev/null 2>&1 + /sbin/chkconfig --del systemtap + %endif +fi +exit 0 + +%postun initscript +# Check whether this is an upgrade of the package. +# If so, restart the service if it's running +if [ "$1" -ge "1" ] ; then + %if %{with_systemd} + /bin/systemctl condrestart systemtap.service >/dev/null 2>&1 || : + %else + /sbin/service systemtap condrestart >/dev/null 2>&1 || : + %endif +fi +exit 0 + +%post runtime-virtguest +%if %{with_systemd} + # Start services if there are ports present + if [ -d /dev/virtio-ports ]; then + (find /dev/virtio-ports -iname 'org.systemtap.stapsh.[0-9]*' -type l \ + | xargs -n 1 basename \ + | xargs -n 1 -I {} /bin/systemctl start stapsh@{}.service) >/dev/null 2>&1 || : + fi +%else + /sbin/chkconfig --add stapshd + /sbin/chkconfig stapshd on + /sbin/service stapshd start >/dev/null 2>&1 || : +%endif +exit 0 + +%preun runtime-virtguest +# Stop service if this is an uninstall rather than an upgrade +if [ $1 = 0 ]; then + %if %{with_systemd} + # We need to stop all stapsh services. Because they are instantiated from + # a template service file, we can't simply call disable. We need to find + # all the running ones and stop them all individually + for service in `/bin/systemctl --full | grep stapsh@ | cut -d ' ' -f 1`; do + /bin/systemctl stop $service >/dev/null 2>&1 || : + done + %else + /sbin/service stapshd stop >/dev/null 2>&1 + /sbin/chkconfig --del stapshd + %endif +fi +exit 0 + +%postun runtime-virtguest +# Restart service if this is an upgrade rather than an uninstall +if [ "$1" -ge "1" ]; then + %if %{with_systemd} + # We need to restart all stapsh services. Because they are instantiated from + # a template service file, we can't simply call restart. We need to find + # all the running ones and restart them all individually + for service in `/bin/systemctl --full | grep stapsh@ | cut -d ' ' -f 1`; do + /bin/systemctl condrestart $service >/dev/null 2>&1 || : + done + %else + /sbin/service stapshd condrestart >/dev/null 2>&1 + %endif +fi +exit 0 + +%if %{with_python3} +%if %{with_systemd} +%preun exporter +if [ $1 = 0 ] ; then + /bin/systemctl stop stap-exporter.service >/dev/null 2>&1 || : + /bin/systemctl disable stap-exporter.service >/dev/null 2>&1 || : +fi +exit 0 + +%postun exporter +# Restart service if this is an upgrade rather than an uninstall +if [ "$1" -ge "1" ]; then + /bin/systemctl condrestart stap-exporter >/dev/null 2>&1 || : +fi +exit 0 +%endif +%endif + +%post +# Remove any previously-built uprobes.ko materials +(make -C %{_datadir}/systemtap/runtime/uprobes clean) >/dev/null 2>&1 || true +(/sbin/rmmod uprobes) >/dev/null 2>&1 || true + +%preun +# Ditto +(make -C %{_datadir}/systemtap/runtime/uprobes clean) >/dev/null 2>&1 || true +(/sbin/rmmod uprobes) >/dev/null 2>&1 || true + +# ------------------------------------------------------------------------ + +%if %{with_java} + +%triggerin runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + if [ -d ${archdir} ]; then + ln -sf %{_libexecdir}/systemtap/libHelperSDT_${arch}.so ${archdir}/libHelperSDT_${arch}.so + ln -sf %{_libexecdir}/systemtap/HelperSDT.jar ${archdir}/../ext/HelperSDT.jar + fi + done +done + +%triggerun runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + rm -f ${archdir}/libHelperSDT_${arch}.so + rm -f ${archdir}/../ext/HelperSDT.jar + done +done + +%triggerpostun runtime-java -- java-1.8.0-openjdk, java-1.7.0-openjdk, java-1.6.0-openjdk +# Restore links for any JDKs remaining after a package removal: +for f in %{_libexecdir}/systemtap/libHelperSDT_*.so; do + %ifarch %{ix86} + arch=i386 + %else + arch=`basename $f | cut -f2 -d_ | cut -f1 -d.` + %endif + for archdir in %{_jvmdir}/*openjdk*/jre/lib/${arch}; do + if [ -d ${archdir} ]; then + ln -sf %{_libexecdir}/systemtap/libHelperSDT_${arch}.so ${archdir}/libHelperSDT_${arch}.so + ln -sf %{_libexecdir}/systemtap/HelperSDT.jar ${archdir}/../ext/HelperSDT.jar + fi + done +done + +# XXX: analogous support for other types of JRE/JDK?? + +%endif + +# ------------------------------------------------------------------------ + +%files +# The master "systemtap" rpm doesn't include any files. + +%files server -f systemtap.lang +%{_bindir}/stap-server +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/stap-serverd +%{_libexecdir}/systemtap/stap-start-server +%{_libexecdir}/systemtap/stap-stop-server +%{_libexecdir}/systemtap/stap-gen-cert +%{_libexecdir}/systemtap/stap-sign-module +%{_libexecdir}/systemtap/stap-authorize-cert +%{_libexecdir}/systemtap/stap-env +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%{_mandir}/man8/stap-server.8* +%if %{with_systemd} +%{_unitdir}/stap-server.service +%{_tmpfilesdir}/stap-server.conf +%else +%{initdir}/stap-server +%dir %{_sysconfdir}/stap-server/conf.d +%config(noreplace) %{_sysconfdir}/sysconfig/stap-server +%endif +%config(noreplace) %{_sysconfdir}/logrotate.d/stap-server +%dir %{_sysconfdir}/stap-server +%dir %attr(0750,stap-server,stap-server) %{_localstatedir}/lib/stap-server +%dir %attr(0700,stap-server,stap-server) %{_localstatedir}/lib/stap-server/.systemtap +%dir %attr(0755,stap-server,stap-server) %{_localstatedir}/log/stap-server +%ghost %config(noreplace) %attr(0644,stap-server,stap-server) %{_localstatedir}/log/stap-server/log +%ghost %attr(0755,stap-server,stap-server) %{_localstatedir}/run/stap-server +%doc README README.unprivileged AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files devel -f systemtap.lang +%{_bindir}/stap +%{_bindir}/stap-prep +%{_bindir}/stap-report +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/runtime +%{_datadir}/systemtap/tapset +%{_mandir}/man1/stap.1* +%{_mandir}/man1/stap-prep.1* +%{_mandir}/man1/stap-report.1* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%doc README README.unprivileged AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING +%if %{with_java} +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/libHelperSDT_*.so +%endif +%if %{with_emacsvim} +%{_emacs_sitelispdir}/*.el* +%{_emacs_sitestartdir}/systemtap-init.el +%{_datadir}/vim/vimfiles/*/*.vim +%endif +# Notice that the stap-resolve-module-function.py file is used by +# *both* the python2 and python3 subrpms. Both subrpms use that same +# python script to help list python probes. +%if %{with_python3_probes} || %{with_python2_probes} +%{_libexecdir}/systemtap/python/stap-resolve-module-function.py +%exclude %{_libexecdir}/systemtap/python/stap-resolve-module-function.py? +%endif + + +%files runtime -f systemtap.lang +%attr(4110,root,stapusr) %{_bindir}/staprun +%{_bindir}/stapsh +%{_bindir}/stap-merge +%{_bindir}/stap-report +%if %{with_dyninst} +%{_bindir}/stapdyn +%endif +%if %{with_bpf} +%{_bindir}/stapbpf +%endif +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/stapio +%{_libexecdir}/systemtap/stap-authorize-cert +%if %{with_crash} +%dir %{_libdir}/systemtap +%{_libdir}/systemtap/staplog.so* +%endif +%{_mandir}/man1/stap-report.1* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%{_mandir}/man8/stapsh.8* +%{_mandir}/man8/staprun.8* +%if %{with_dyninst} +%{_mandir}/man8/stapdyn.8* +%endif +%if %{with_bpf} +%{_mandir}/man8/stapbpf.8* +%endif +%doc README README.security AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files client -f systemtap.lang +%doc README README.unprivileged AUTHORS NEWS +%{_datadir}/systemtap/examples +%{!?_licensedir:%global license %%doc} +%license COPYING +%doc docs.installed/*.pdf +%if %{with_docs} +%if %{with_htmldocs} +%doc docs.installed/tapsets/*.html +%doc docs.installed/SystemTap_Beginners_Guide +%endif +%endif +%{_bindir}/stap +%{_bindir}/stap-prep +%{_bindir}/stap-report +%{_mandir}/man1/stap.1* +%{_mandir}/man1/stap-prep.1* +%{_mandir}/man1/stap-merge.1* +%{_mandir}/man1/stap-report.1* +%{_mandir}/man1/stapref.1* +%{_mandir}/man3/* +%{_mandir}/man7/error* +%{_mandir}/man7/stappaths.7* +%{_mandir}/man7/warning* +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/tapset + + + +%files initscript +%if %{with_systemd} +%{_unitdir}/systemtap.service +%{_sbindir}/systemtap-service +%else +%{initdir}/systemtap +%{_sbindir}/systemtap-service +%endif +%dir %{_sysconfdir}/systemtap +%dir %{_sysconfdir}/systemtap/conf.d +%dir %{_sysconfdir}/systemtap/script.d +%config(noreplace) %{_sysconfdir}/systemtap/config +%dir %{_localstatedir}/cache/systemtap +%ghost %{_localstatedir}/run/systemtap +%{_mandir}/man8/systemtap-service.8* +%if %{with_dracut} + %dir %{dracutstap} + %{dracutstap}/* +%endif + + +%files sdt-devel +%{_bindir}/dtrace +%{_includedir}/sys/sdt.h +%{_includedir}/sys/sdt-config.h +%{_mandir}/man1/dtrace.1* +%{_rpmmacrodir}/macros.systemtap +%doc README AUTHORS NEWS +%{!?_licensedir:%global license %%doc} +%license COPYING + + +%files testsuite +%dir %{_datadir}/systemtap +%{_datadir}/systemtap/testsuite + + +%if %{with_java} +%files runtime-java +%dir %{_libexecdir}/systemtap +%{_libexecdir}/systemtap/libHelperSDT_*.so +%{_libexecdir}/systemtap/HelperSDT.jar +%{_libexecdir}/systemtap/stapbm +%endif + +%if %{with_python2_probes} +%files runtime-python2 +%{python_sitearch}/HelperSDT +%{python_sitearch}/HelperSDT-*.egg-info +%endif +%if %{with_python3_probes} +%files runtime-python3 +%{python3_sitearch}/HelperSDT +%{python3_sitearch}/HelperSDT-*.egg-info +%endif + +%if %{with_virthost} +%files runtime-virthost +%{_mandir}/man1/stapvirt.1* +%{_bindir}/stapvirt +%endif + +%if %{with_virtguest} +%files runtime-virtguest +%if %{with_systemd} + %{udevrulesdir}/99-stapsh.rules + %{_unitdir}/stapsh@.service +%else + %{udevrulesdir}/99-stapsh-init.rules + %dir %{_libexecdir}/systemtap + %{_libexecdir}/systemtap/stapsh-daemon + %{initdir}/stapshd + %{_sysconfdir}/sysconfig/modules/virtio_console.modules +%endif +%endif + +%if %{with_python3} +%files exporter +%{_sysconfdir}/stap-exporter +%{_sysconfdir}/sysconfig/stap-exporter +%{_unitdir}/stap-exporter.service +%{_mandir}/man8/stap-exporter.8* +%{_sbindir}/stap-exporter +%endif + +# ------------------------------------------------------------------------ + +# Future new-release entries should be of the form +# * DDD MMM DD YYYY YOURNAME - V-R +# - Upstream release, see wiki page below for detailed notes. +# http://sourceware.org/systemtap/wiki/SystemTapReleases + +# PRERELEASE +%changelog +* Tue Jan 26 2021 Frank Ch. Eigler - 4.4-9 +- rhbz1927497 enable debuginfod client for buildid probing + +* Tue Jan 26 2021 Frank Ch. Eigler - 4.4-8 +- rhbz1902696 fix invocation as stapusr with procfs/lockdown + +* Wed Jan 20 2021 Martin Cermak - 4.4-7 +- rhbz1650594 fix boot time probing feature + +* Mon Jan 04 2021 Frank Ch. Eigler - 4.4-6 +- rhbz1906662 backport transport/utrace/locking patches + +* Thu Dec 17 2020 Frank Ch. Eigler - 4.4-5 +- rhbz1908904 fix lock-pushdown codegen for conditional probes + +* Tue Dec 15 2020 Frank Ch. Eigler - 4.4-4 +- rhbz1902696 fix invocation as stapusr vs. root + +* Tue Nov 17 2020 Frank Ch. Eigler - 4.4-3 +- rhbz1873492 related: rhel8 kernel_is_locked_down detection + +* Mon Nov 16 2020 Frank Ch. Eigler - 4.4-2 +- rhbz1898288: stability for exhausted UTRACE_TASK_WORK_POOL +- rhbz1873492 related: mokutil parser robustness for RH keys + +* Mon Nov 09 2020 Frank Ch. Eigler - 4.4-1 +- Upstream release. + +* Wed Aug 12 2020 Martin Cermak - 4.3-4 +- rhbz1868095: Refix including PR26379. + +* Wed Aug 12 2020 Martin Cermak - 4.3-3 +- rhbz1868095: byteman-java-methods-probing + +* Tue Jul 28 2020 Frank Ch. Eigler - 4.3-2 +- rhbz1857749: uprobes-inode changes +- rhbz1855264: @cast adaptations + +* Thu Jun 11 2020 Stan Cox - 4.3-1 +- rhbz1804319: Upstream release. + +* Tue Feb 11 2020 Frank Ch. Eigler - 4.2-6 +- rhbz1795196 cont'd: autoconf fix for kernel change to stack_trace_save_regs + +* Tue Jan 28 2020 Frank Ch. Eigler - 4.2-5 +- rhbz1795196: kallsyms vs. elfutils/vmlinuz + +* Thu Jan 23 2020 Frank Ch. Eigler - 4.2-4 +- rhbz1788662: check for rcu_is_watching() during prologue + +* Fri Jan 10 2020 Frank Ch. Eigler - 4.2-3 +- rhbz1788544: stop bytecompiling systemtap-mode.el + +* Fri Jan 10 2020 Frank Ch. Eigler - 4.2-2 +- rhbz1788648: aarch64 sdt.h parse [x0, x1] + +* Tue Nov 19 2019 Frank Ch. Eigler - 4.2-1 +- rhbz1744989: Upstream release. + +* Tue Aug 06 2019 Frank Ch. Eigler - 4.1-6 +- rhbz1732514: add java-devel prereq to systemtap-runtime-java + +* Wed Jul 31 2019 Serguei Makarov - 4.1-5 +- rhbz1734973: Fix possible stapbpf segfault with foreach string key iteration. + +* Mon Jul 29 2019 Frank Ch. Eigler - 4.1-4 +- rhbz1732173 (arm64 syscall parameters) + +* Tue Jun 04 2019 Stan Cox - 4.1-3 +- Rebuilt for dyninst 10.1.0 + +* Tue May 14 2019 Frank Ch. Eigler - 4.1-2 +- Correct GNU_parameter_ref dwarf feature typo. + +* Tue May 07 2019 Serguei Makarov - 4.1-1 +- Upstream release. + +* Mon Dec 17 2018 Frank Ch. Eigler - 4.0-7 +- rhbz1657909: vfs tapset fixes for $cred + +* Mon Dec 10 2018 William Cohen - 4.0-6 +- rhbz1657857: Please, backport periodic.stp from current upstream aacee6563. + +* Fri Dec 07 2018 Frank Ch. Eigler - 4.0-5 +- rhbz1657186: fix/port nfsd.proc4.[read,commit] tapset aliases + +* Thu Dec 06 2018 Frank Ch. Eigler - 4.0-4 +- rhbz1656795: support bdflush syscall + +* Tue Dec 04 2018 Frank Ch. Eigler - 4.0-3 +- rhbz1655631: change kernel-devel* deps to Recommends: + +* Wed Nov 14 2018 Serhei Makarov - 4.0-2 +- rhbz1643997: backported string tapset functionality and bugfixes + +* Sat Oct 13 2018 Frank Ch. Eigler - 4.0-1 +- Upstream release. + +* Thu Aug 30 2018 Frank Ch. Eigler - 4.0-0.20180830git +- Enable brp-mangle for python scripts (rhbz1619413). + +* Fri Aug 10 2018 Frank Ch. Eigler - 4.0-0.20180810git +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Mon May 14 2018 Frank Ch. Eigler - 3.3-0.20180508git9c6ac6cda49e.2 +- respin w/ fixed glibc + +* Tue May 08 2018 Frank Ch. Eigler - 3.3-0.20180508git9c6ac6cda49e +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Thu Apr 19 2018 Mark Wielaard - 3.2-11 +- Add rhbz1549063.patch (/bin/env -> /usr/bin/env) +- Add rhbz1566745.patch (4.15 kernel tapset updates) +- Enable accidentially disabled Suggests: kernel-devel again. + +* Wed Apr 18 2018 Mark Wielaard - 3.2-10 +- Add unwind-fallback.patch +- rhbz1566422.patch + +* Tue Apr 17 2018 Mark Wielaard - 3.2-9 +- Add unwind-fallback.patch. + +* Tue Mar 27 2018 Björn Esser - 3.2-8 +- Rebuilt for libjson-c.so.4 (json-c v0.13.1) on fc28 + +* Thu Feb 22 2018 Sergey Avseyev - 3.2-7 +- rhbz1546563 (backport fix for removed timers in kernel 4.15) + +* Tue Feb 13 2018 Stan Cox - 3.2-6 +- rebuilt + +* Fri Feb 09 2018 Igor Gnatenko - 3.2-5 +- Escape macros in %%changelog + +* Wed Feb 07 2018 Igor Gnatenko - 3.2-4 +- Fix very old Requires + +* Sun Dec 10 2017 Björn Esser - 3.2-3 +- Rebuilt for libjson-c.so.3 + +* Fri Oct 20 2017 Frank Ch. Eigler - 3.2-2 +- rhbz1504009 (dtrace -G -o /dev/null) + +* Wed Oct 18 2017 Frank Ch. Eigler - 3.2-1 +- Upstream release. + +* Fri Aug 11 2017 Igor Gnatenko - 3.2-0.20170516gitc67d8f274b21 +- Rebuilt after RPM update (№ 3) + +* Thu Aug 10 2017 Igor Gnatenko - 3.2-0.20170515gitc67d8f274b21 +- Rebuilt for RPM soname bump + +* Thu Aug 03 2017 Fedora Release Engineering - 3.2-0.20170514gitc67d8f274b21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 3.2-0.20170513gitc67d8f274b21 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri May 12 2017 Frank Ch. Eigler - 3.2-0.20170512gitc67d8f274b21 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Mon Apr 10 2017 Frank Ch. Eigler - 3.2-0.20170410gitcbf2583808d6 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Thu Apr 06 2017 Frank Ch. Eigler - 3.2-0.20170406git83d186dc7f5c +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Tue Mar 21 2017 Frank Ch. Eigler - 3.2-0.20170321git272146660f54 +- Automated weekly rawhide release +- Applied spec changes from upstream git + +* Fri Mar 10 2017 Stan Cox - 3.1-2 +- Rebuild for dyninst 9.3 + +* Fri Feb 17 2017 Frank Ch. Eigler - 3.1-1 +- Upstream release. + +* Sun Mar 27 2016 Frank Ch. Eigler - 3.0-1 +- Upstream release. + +* Thu Oct 08 2015 Frank Ch. Eigler - 2.9-1 +- Upstream release. + +* Wed Jun 17 2015 Abegail Jakop - 2.8-1 +- Upstream release. + +* Wed Feb 18 2015 Frank Ch. Eigler - 2.7-1 +- Upstream release. + +* Fri Sep 05 2014 Josh Stone - 2.6-1 +- Upstream release. + +* Mon Jul 07 2014 Josh Stone +- Flip with_dyninst to an %%ifarch whitelist. + +* Wed Apr 30 2014 Jonathan Lebon - 2.5-1 +- Upstream release. + +* Thu Feb 13 2014 Lukas Berk +- Add directory checks for runtime-java sym links + +* Mon Jan 06 2014 Jonathan Lebon +- Added dracut module to initscript package + +* Wed Nov 06 2013 Frank Ch. Eigler - 2.4-1 +- Upstream release. + +* Wed Oct 09 2013 Jonathan Lebon +- Added runtime-virthost and runtime-virtguest packages. + +* Thu Jul 25 2013 Frank Ch. Eigler - 2.3-1 +- Upstream release. + +* Thu May 16 2013 Frank Ch. Eigler - 2.2.1-1 +- Upstream release. + +* Tue May 14 2013 Frank Ch. Eigler - 2.2-1 +- Upstream release. + +* Wed Feb 13 2013 Serguei Makarov - 2.1-1 +- Upstream release. + +* Tue Oct 09 2012 Josh Stone - 2.0-1 +- Upstream release. + +* Fri Jul 13 2012 Peter Robinson +- Fix ifarch statement +- use file based requires for glibc-devel on x86_64 so that we work in koji + +* Sun Jun 17 2012 Frank Ch. Eigler - 1.8-1 +- Upstream release. + +* Wed Feb 01 2012 Frank Ch. Eigler - 1.7-1 +- Upstream release. + +* Fri Jan 13 2012 David Smith - 1.6-2 +- Fixed /bin/mktemp require. + +* Mon Jul 25 2011 Stan Cox - 1.6-1 +- Upstream release. + +* Mon May 23 2011 Stan Cox - 1.5-1 +- Upstream release. + +* Mon Jan 17 2011 Frank Ch. Eigler - 1.4-1 +- Upstream release. + +* Wed Jul 21 2010 Josh Stone - 1.3-1 +- Upstream release. + +* Mon Mar 22 2010 Frank Ch. Eigler - 1.2-1 +- Upstream release. + +* Mon Dec 21 2009 David Smith - 1.1-1 +- Upstream release. + +* Tue Sep 22 2009 Josh Stone - 1.0-1 +- Upstream release. + +* Tue Aug 4 2009 Josh Stone - 0.9.9-1 +- Upstream release. + +* Thu Jun 11 2009 Josh Stone - 0.9.8-1 +- Upstream release. + +* Thu Apr 23 2009 Josh Stone - 0.9.7-1 +- Upstream release. + +* Fri Mar 27 2009 Josh Stone - 0.9.5-1 +- Upstream release. + +* Wed Mar 18 2009 Will Cohen - 0.9-2 +- Add location of man pages. + +* Tue Feb 17 2009 Frank Ch. Eigler - 0.9-1 +- Upstream release. + +* Thu Nov 13 2008 Frank Ch. Eigler - 0.8-1 +- Upstream release. + +* Tue Jul 15 2008 Frank Ch. Eigler - 0.7-1 +- Upstream release. + +* Fri Feb 1 2008 Frank Ch. Eigler - 0.6.1-3 +- Add zlib-devel to buildreq; missing from crash-devel +- Process testsuite .stp files for #!stap->#!/usr/bin/stap + +* Fri Jan 18 2008 Frank Ch. Eigler - 0.6.1-1 +- Add crash-devel buildreq to build staplog.so crash(8) module. +- Many robustness & functionality improvements: + +* Wed Dec 5 2007 Will Cohen - 0.6-2 +- Correct Source to point to location contain code. + +* Thu Aug 9 2007 David Smith - 0.6-1 +- Bumped version, added libcap-devel BuildRequires. + +* Wed Jul 11 2007 Will Cohen - 0.5.14-2 +- Fix Requires and BuildRequires for sqlite. + +* Mon Jul 2 2007 Frank Ch. Eigler - 0.5.14-1 +- Many robustness improvements: 1117, 1134, 1305, 1307, 1570, 1806, + 2033, 2116, 2224, 2339, 2341, 2406, 2426, 2438, 2583, 3037, + 3261, 3282, 3331, 3428 3519, 3545, 3625, 3648, 3880, 3888, 3911, + 3952, 3965, 4066, 4071, 4075, 4078, 4081, 4096, 4119, 4122, 4127, + 4146, 4171, 4179, 4183, 4221, 4224, 4254, 4281, 4319, 4323, 4326, + 4329, 4332, 4337, 4415, 4432, 4444, 4445, 4458, 4467, 4470, 4471, + 4518, 4567, 4570, 4579, 4589, 4609, 4664 + +* Mon Mar 26 2007 Frank Ch. Eigler - 0.5.13-1 +- An emergency / preliminary refresh, mainly for compatibility + with 2.6.21-pre kernels. + +* Mon Jan 1 2007 Frank Ch. Eigler - 0.5.12-1 +- Many changes, see NEWS file. + +* Tue Sep 26 2006 David Smith - 0.5.10-1 +- Added 'systemtap-runtime' subpackage. + +* Wed Jul 19 2006 Roland McGrath - 0.5.9-1 +- PRs 2669, 2913 + +* Fri Jun 16 2006 Roland McGrath - 0.5.8-1 +- PRs 2627, 2520, 2228, 2645 + +* Fri May 5 2006 Frank Ch. Eigler - 0.5.7-1 +- PRs 2511 2453 2307 1813 1944 2497 2538 2476 2568 1341 2058 2220 2437 + 1326 2014 2599 2427 2438 2465 1930 2149 2610 2293 2634 2506 2433 + +* Tue Apr 4 2006 Roland McGrath - 0.5.5-1 +- Many changes, affected PRs include: 2068, 2293, 1989, 2334, + 1304, 2390, 2425, 953. + +* Wed Feb 1 2006 Frank Ch. Eigler - 0.5.4-1 +- PRs 1916, 2205, 2142, 2060, 1379 + +* Mon Jan 16 2006 Roland McGrath - 0.5.3-1 +- Many changes, affected PRs include: 2056, 1144, 1379, 2057, + 2060, 1972, 2140, 2148 + +* Mon Dec 19 2005 Roland McGrath - 0.5.2-1 +- Fixed build with gcc 4.1, various tapset changes. + +* Wed Dec 7 2005 Roland McGrath - 0.5.1-1 +- elfutils update, build changes + +* Fri Dec 02 2005 Frank Ch. Eigler - 0.5-1 +- Many fixes and improvements: 1425, 1536, 1505, 1380, 1329, 1828, 1271, + 1339, 1340, 1345, 1837, 1917, 1903, 1336, 1868, 1594, 1564, 1276, 1295 + +* Mon Oct 31 2005 Roland McGrath - 0.4.2-1 +- Many fixes and improvements: PRs 1344, 1260, 1330, 1295, 1311, 1368, + 1182, 1131, 1332, 1366, 1456, 1271, 1338, 1482, 1477, 1194. + +* Wed Sep 14 2005 Roland McGrath - 0.4.1-1 +- Many fixes and improvements since 0.2.2; relevant PRs include: + 1122, 1134, 1155, 1172, 1174, 1175, 1180, 1186, 1187, 1191, 1193, 1195, + 1197, 1205, 1206, 1209, 1213, 1244, 1257, 1258, 1260, 1265, 1268, 1270, + 1289, 1292, 1306, 1335, 1257 + +* Wed Sep 7 2005 Frank Ch. Eigler +- Bump version. + +* Tue Aug 16 2005 Frank Ch. Eigler +- Bump version. + +* Wed Aug 3 2005 Martin Hunt - 0.2.2-1 +- Add directory /var/cache/systemtap +- Add stp_check to /usr/libexec/systemtap + +* Wed Aug 3 2005 Roland McGrath - 0.2.1-1 +- New version 0.2.1, various fixes. + +* Fri Jul 29 2005 Roland McGrath - 0.2-1 +- New version 0.2, requires elfutils 0.111 + +* Mon Jul 25 2005 Roland McGrath +- Clean up spec file, build bundled elfutils. + +* Thu Jul 21 2005 Martin Hunt +- Set Version to use version from autoconf. +- Fix up some of the path names. +- Add Requires and BuildRequires. + +* Tue Jul 19 2005 Will Cohen +- Initial creation of RPM.